将超时设置为操作

Anw*_*dra 41 c# multithreading timeout

我有obj第三方组件的对象,

// this could take more than 30 seconds
int result = obj.PerformInitTransaction(); 
Run Code Online (Sandbox Code Playgroud)

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

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

Pao*_*sco 73

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

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.");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个阻塞调用,如果你需要主线程做其他事情,此时这不会工作! (2认同)

Col*_*nic 13

更简单地使用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");
Run Code Online (Sandbox Code Playgroud)


Chr*_*s S 7

如果您不想阻止主线程,可以使用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();
}
Run Code Online (Sandbox Code Playgroud)

Jon Skeet有一种不那么有力的方式(优雅关闭工作线程)停止线程而不是中止.

但是,由于您无法控制操作PerformInitTransaction(),因此当Abort失败并使对象处于无效状态时,您无法做很多事情.如上所述,如果你能够清除任何中止PerformInitTransaction悬挂的东西,你可以通过抓住来做到这一点ThreadAbortException,虽然这是第三方通话,但这意味着猜测你已经离开他们的方法的状态.

PerformInitTransaction确实应该是一个提供超时.


小智 5

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

对于操作(无返回值):

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;
}
Run Code Online (Sandbox Code Playgroud)

对于 Func(有返回值):

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;
}
Run Code Online (Sandbox Code Playgroud)