将超时设置为操作

时间:2010-02-15 11:00:02

标签: c# multithreading timeout

我有对象obj,它是第三方组件,

// this could take more than 30 seconds
int result = obj.PerformInitTransaction(); 

我不知道里面发生了什么。 我所知道的是,如果需要更长的时间,那就失败了。

如何为此操作设置超时机制,如果超过30秒,我只是抛出MoreThan30SecondsException

10 个答案:

答案 0 :(得分:72)

您可以在单独的线程中运行该操作,然后在线程连接操作上设置超时:

using System.Threading;

class Program {
    static void DoSomething() {
        try {
            // your call here...
            obj.PerformInitTransaction();         
        } catch (ThreadAbortException) {
            // cleanup code, if needed...
        }
    }

    public static void Main(params string[] args) {

        Thread t = new Thread(DoSomething);
        t.Start();
        if (!t.Join(TimeSpan.FromSeconds(30))) {
            t.Abort();
            throw new Exception("More than 30 secs.");
        }
    }
}

答案 1 :(得分:12)

更简单地使用Task.Wait(TimeSpan)

using System.Threading.Tasks;

var task = Task.Run(() => obj.PerformInitTransaction());
if (task.Wait(TimeSpan.FromSeconds(30)))
    return task.Result;
else
    throw new Exception("Timed out");

答案 2 :(得分:6)

如果您不想阻止主线程,可以使用System.Threading.Timer

private Thread _thread;

void Main(string[] args)
{
    _thread = new ThreadStart(ThreadEntry);
    _thread.Start();
    Timer timer = new Timer(Timeout,null,30000,Timeout.Infinite);
}


void ThreadEntry()
{
    int result = obj.PerformInitTransaction(); 
}

void TimeOut(object state)
{
    // Abort the thread - see the comments
    _thread.Abort();

    throw new ItTimedOutException();
}

Jon Skeet停止线程的方式( Shutting Down Worker Threads Gracefully )不如中止。

但是,由于您无法控制PerformInitTransaction()正在执行的操作,因此当Abort失败并使对象处于无效状态时,您无法做很多事情。如上所述,如果您能够清除任何导致PerformInitTransaction中止的内容,那么您可以通过捕获ThreadAbortException来执行此操作,但因为它是第三方调用它将意味着猜测您的状态'我们把他们的方法留在了。

PerformInitTransaction应该是提供超时的那个。

答案 3 :(得分:2)

你需要注意中止这样的操作,尤其是因为它(可能)没有访问要修改的代码的第三方组件。

如果您中止操作,那么您将不知道您已经离开基础类的状态。例如,它可能已获得锁定,而您的约会导致该锁定未被释放。即使您在中止操作后销毁该对象,它也可能已经改变了一些全局状态,因此您将无法在不重新启动的情况下可靠地创建新实例。

答案 4 :(得分:1)

您可能会考虑在线程中调用该方法,并在超时时中止线程并引发异常。此外,在这种情况下,您必须处理ThreadBorted异常。

答案 5 :(得分:1)

以下是两个实现,它们也会抛出内部任务中发生的任何异常。

对于操作(无返回值):

public static bool DoWithTimeout(Action action, int timeout)
{
    Exception ex = null;
    CancellationTokenSource cts = new CancellationTokenSource();
    Task task = Task.Run(() =>
    {
        try
        {
            using (cts.Token.Register(Thread.CurrentThread.Abort))
            {
                action();
            }
        }
        catch (Exception e)
        {
            if (!(e is ThreadAbortException))
                ex = e;
        }
    }, cts.Token);
    bool done = task.Wait(timeout);
    if (ex != null)
        throw ex;
    if (!done)
        cts.Cancel();
    return done;
}

对于Funcs(带有返回值):

public static bool DoWithTimeout<T>(Func<T> func, int timeout, out T result)
{
    Exception ex = null;
    result = default(T);
    T res = default(T);
    CancellationTokenSource cts = new CancellationTokenSource();
    Task task = Task.Run(() =>
    {
        try
        {
            using (cts.Token.Register(Thread.CurrentThread.Abort))
            {
                res = func();
            }
        }
        catch (Exception e)
        {
            if (!(e is ThreadAbortException))
                ex = e;
        }
    }, cts.Token);
    bool done = task.Wait(timeout);
    if (ex != null)
        throw ex;
    if (done)
        result = res;
    else
        cts.Cancel();
    return done;
}

答案 6 :(得分:0)

使用辅助类here有一个很好的通用解决方案示例。

它使用Action委托来避免上一个示例中显示的Thread创建/销毁。

我希望这会有所帮助。

答案 7 :(得分:0)

这就是我要用的。与javascript超时的工作方式类似。

public class Toolz {
    public static System.Threading.Tasks.Task<object> SetTimeout(Func<object> func, int secs) {
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(secs));
        return System.Threading.Tasks.Task.Run(() => func());
    }
}

class Program {
    static void Main(string[] args) {
        Console.WriteLine(DateTime.Now);
        Toolz.SetTimeout(() => {
            Console.WriteLine(DateTime.Now);
            return "";
        }, 10);
    }

}

答案 8 :(得分:0)

我认为这是最简单的:

using System.Threading.Tasks;

var timeToWait = 30000; //ms
Task.Run(async () =>
{
    await Task.Delay(timeToWait);
    //do your timed task i.e. --
    int result = obj.PerformInitTransaction(); 
});

答案 9 :(得分:0)

我只是在.NET 4.0应用程序中遇到了此问题(无法访问Task.RunTask.Delay等)。如果您要原谅最后一行(这是setTimeout部分),则非常简洁。

int sleepTime = 10000;
Action myAction = () => {
    // my awesome cross-thread update code    
    this.BackColor = Color.Red;
};
new System.Threading.Thread(() => { System.Threading.Thread.Sleep(sleepTime); if (InvokeRequired) myAction(); else myAction(); }).Start();
相关问题