我有以下代码,其中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);
}
}
吗?
答案 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);
}
}