在继续执行之前等待已取消的任务完成

时间:2014-08-28 03:51:21

标签: c# design-patterns asynchronous task cancellation

我有以下代码,其中Task可以取消,但我基本上需要等待它完成(以确保完整性),然后将OperationCanceledException抛给调用者。

public static void TaskCancellationTest() {
    try {
        Console.WriteLine("TaskCancellationTest started.");
        var cts = new CancellationTokenSource();
        var t = Task.Run(() => {
            if (cts.Token.IsCancellationRequested) return;
            Console.WriteLine("1");
            Task.Delay(2000).Wait();
            Console.WriteLine("2");
        }).ContinueWith(task => {
            if (cts.Token.IsCancellationRequested) return;
            Console.WriteLine("3");
            Task.Delay(2000).Wait();
            Console.WriteLine("4");
        });
        Task.Run(() => {
            Task.Delay(1000).Wait();
            Console.WriteLine("Cancelling...");
            cts.Cancel();
        });
        t.Wait();
        try {
            cts.Token.ThrowIfCancellationRequested();
        }
        catch (OperationCanceledException) {
            Console.WriteLine("Gracefully canceled.");
        }
        Console.WriteLine("TaskCancellationTest completed.");
    }
    catch (Exception ex) {
        Console.WriteLine("TaskCancellationTest... Failure: " + ex);
    }
}

结果如预期的那样:

1
Cancelling...
2
Gracefully canceled.

它有效,但我更愿意将CancellationToken传递给方法,因为我知道这是一个更好的模式。我还希望能够观察方法体内的令牌并调用ThrowIfCancellationRequested()中止,而不必等待下一个ContinueWith()。

我正在使用以下替代代码,这也有效,但是有没有办法让OperationCanceledException代替AggregateException

如果我将cancellationToken传递给WaitAll()方法,问题是它会在取消令牌后立即抛出OperationCanceledException,而不是等待任务t1和{ {1}}实际完成(它们将继续在后台运行),然后只抛出异常。

t2

我准备了一个小提琴here

This question的标题与我的标题非常相似,但遗憾的是,接受的答案与我的情况无关。

你知道有什么方法可以实现我想要的,尽可能好地利用public static void TaskCancellationTest2() { try { Console.WriteLine("TaskCancellationTest2 started."); var cts = new CancellationTokenSource(); var t1 = Task.Run(() => { Console.WriteLine("1"); Task.Delay(2000).Wait(); Console.WriteLine("2"); }, cts.Token); var t2 = t1.ContinueWith(task => { Console.WriteLine("3"); Task.Delay(2000).Wait(); cts.Token.ThrowIfCancellationRequested(); Console.WriteLine("4"); }, cts.Token); Task.Run(() => { Task.Delay(1000).Wait(); Console.WriteLine("Cancelling..."); cts.Cancel(); }); try { try { Task.WaitAll(t1, t2); } catch (AggregateException ae) { if (ae.InnerExceptions.Count == 1 && ae.InnerExceptions.Single() is OperationCanceledException) { throw ae.InnerExceptions.Single(); } throw; } } catch (OperationCanceledException) { Console.WriteLine("Gracefully canceled."); } Console.WriteLine("TaskCancellationTest2 completed."); } catch (Exception ex) { Console.WriteLine("TaskCancellationTest2... Failure: " + ex); } } 吗?

1 个答案:

答案 0 :(得分:1)

我认为如果设置CancellationToken,TPL旨在热切地完成任务。您看到此行为的部分原因是因为您正在调用t.Wait(cts.Token)。即使任务没有运行完成,如果设置了令牌,则CancellationToken的重载将停止等待。

ContinueWith相同如果您传入CancellationToken,则只要设置该令牌,任务即可完成。

将代码更改为在没有令牌的情况下致电t.Wait()ContinueWith,您就可以获得所需的行为。

    public static void TaskCancellationTestNotWorking1()
    {
        try
        {
            Console.WriteLine("TaskCancellationTestNotWorking started.");
            var cts = new CancellationTokenSource();
            var t = Task.Run(() =>
            {
                Console.WriteLine("1");
                Thread.Sleep(2000);
                Console.WriteLine("2");
            }, cts.Token).ContinueWith(task =>
            {
                Console.WriteLine("3");
                Thread.Sleep(2000);
                cts.Token.ThrowIfCancellationRequested();
                Console.WriteLine("4");
            });

            Task.Run(() =>
            {
                Thread.Sleep(1000);
                Console.WriteLine("Cancelling...");
                cts.Cancel();
            }, cts.Token);

            try
            {
                t.Wait();
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("IsCanceled " + t.IsCanceled);
                Console.WriteLine("IsCompleted " + t.IsCompleted);                    

                Console.WriteLine("Gracefully canceled.");
            }
            catch (AggregateException)
            {
                Console.WriteLine("IsCanceled " + t.IsCanceled);
                Console.WriteLine("IsCompleted " + t.IsCompleted);

                Console.WriteLine("Gracefully canceled 1.");
            }

            Console.WriteLine("TaskCancellationTestNotWorking completed.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("TaskCancellationTestNotWorking... Failure: " + ex);
        }
    }

您可能会发现此文章有用How do I cancel non-cancelable async operations?