Framework 4.0 上的任务:在延续链中的最内层任务上设置超时

Tasks on Framework 4.0: Put a timeout on the innermost task in a chain of continuations

我一直在寻找一种基于任务的方法来检测任务超时,而无需 Wait()

超时应该放在连续链中最内层的任务,而异常应该只在最外层被捕获。

我的解决方案不是阻止执行,而是 returns 包装原始任务的任务,允许用户在超时时捕获异常。

所以我想出了这个代码:

public static Task<T> Timeout<T>(Task<T> baseTask,  int milliseconds)
{
    var timer = Delay(milliseconds);

    return Task.Factory.ContinueWhenAny(
        new []
        {
            baseTask,
            timer.ContinueWith<T>(x => { throw new TaskTimeOutException(); })
        },
        x => x.Result
    );
}

函数 Delay()How to put a task to sleep (or delay) in C# 4.0? 的公认解决方案中进行了描述。

我想改进我的代码,基本上我有几个问题:

谢谢。

编辑

根据评论,我做出了这个轻微的改进:

public static Task<T> Timeout<T>(Task<T> baseTask,  int milliseconds)
{
    var timer = Delay(milliseconds);

    return Task.Factory.ContinueWhenAny(
        new []
        {
            baseTask,
            timer
        },
        x =>
        {
            if (x.Equals(baseTask)) return baseTask.Result;
            throw new TaskTimeOutException();
        }
    );
}

您可以尝试使用 CancellationToken 创建任务,然后像这样调用 tokenSource.CancelAfter(...)

var tokeSource = new CancellationTokenSource();
Task.Run(() => { Console.WriteLine("processing"); }, tokenSource.Token);
tokenSource.CancelAfter(TimeSpan.FromSeconds(30));

在 .Net 4.0 中,您可以自己实现 CancelAfter,例如

public static class CancellationTokenSourceExtensions
{
    public static Task CancelAfter(this CancellationTokenSource cts, TimeSpan timeout)
    {
        return Task.Delay(timeout).ContinueWith(t => cts.Cancel());
    }
}

个人认为基于cts的方案更符合TPL精神

class Program
    {
        static bool continueRun = true;
        static void Main(string[] args)
        {
            System.AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;

            try
            {
                Console.WriteLine("Enter code");
                Task.Run(() =>
                {
                    Console.WriteLine("Enter task");
                    System.Threading.Thread.Sleep(1000);
                    Console.WriteLine("End thread sleep");
                    throw new Exception("Inner task execution");
                });
                Console.WriteLine("Exit code");
            }
            catch (Exception err)
            {
                Console.WriteLine("Exception code");
            }
            finally
            {
                Console.WriteLine("Finally code");
            }


            Console.WriteLine("Press a key to exit");
            Console.ReadLine();
        }

        private static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e)
        {
            Console.Write("Unhandled exception");
            continueRun = false;
            Console.ReadLine();
        }
    }

输出:

Enter code
Exit  code
Finally code
Press a key to exit
Enter Task
End thread sleep

在 visual studio 中,您会看到引发一个未捕获的异常