等待取消的任务完成后再继续执行
本文关键字:再继续 执行 取消 任务 等待 | 更新日期: 2023-09-27 18:19:37
我有以下代码,其中可以取消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
吗?
如果我将cancelloken传递给WaitAll()
方法,问题是它会在取消令牌后立即抛出OperationCanceledException
,而不是等待任务t1
和t2
实际完成(它们将继续在后台运行),然后只抛出异常。
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);
}
}
我在这里准备了一把小提琴。
这个问题的标题与我的非常相似,但不幸的是,公认的答案与我的情况无关。
你知道有什么方法可以实现我想要的,尽可能好地利用CancellationToken
吗?
我认为如果设置了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);
}
}
您可能会发现这篇文章很有用。如何取消不可取消的异步操作?