C# try/catch(OperationCanceledException)与ContinueWith()的比较
Microsoft将通过使用C# try/catch(OperationCanceledException)与ContinueWith()的比较,c#,asynchronous,async-await,C#,Asynchronous,Async Await,Microsoft将通过使用try/catch和OperationCanceledException来处理已取消的任务。同时,可以使用.ContinueWith()将执行的任务包装为continuation,它将吞下操作取消异常而不会抛出。看起来continuation仍然在内部处理异常,但并没有使异常冒泡 给定热执行路径上的可取消任务(接收取消令牌),是否仍建议使用try/catch(OperationCanceledException)或continuation方法 例如: await Ta
try/catch
和OperationCanceledException
来处理已取消的任务。同时,可以使用.ContinueWith()
将执行的任务包装为continuation,它将吞下操作取消异常
而不会抛出。看起来continuation仍然在内部处理异常,但并没有使异常冒泡
给定热执行路径上的可取消任务(接收取消令牌),是否仍建议使用try/catch(OperationCanceledException)
或continuation方法
例如:
await Task.Delay(delayValue, cts.Token);
可以通过
try
{
await Task.Delay(delayValue, cts.Token);
}
catch(OperationCanceledException)
{
// token triggered cancellation
return;
}
或通过
var task = await Task.Delay(delayValue, cts.Token).ContinueWith(t => t);
if (task.IsCancelled)
{
// token triggered cancellation
return;
}
幕后
wait
会将代码分为启动任务和继续任务。许多等待将导致许多任务。这是使用async/await的最大优势——您不必处理这种复杂性。最好不要把它们混在一起。无论如何,等待很简单,你不能添加太多的选项<另一方面,code>ContinueWith有几个选项。其中包括允许您添加的重载。在不知道最终目的是什么的情况下,您可以同时使用continuation和exception:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
try
{
await Task.Delay(TimeSpan.FromSeconds(5), cts.Token)
.ContinueWith(t => Console.WriteLine("Continued"), TaskContinuationOptions.NotOnCanceled);
Console.WriteLine("Hihi...");
}
catch (OperationCanceledException)
{
Console.WriteLine("Cancelled");
}
Console.ReadLine();
严格按照问题而不是动机,我使用BenchmarkDotNet运行了一个基准测试:
[MemoryDiagnoser]
public class CancelBench
{
private int delayValue = 15;
private CancellationToken cancellationToken = new CancellationToken(true);
[Benchmark]
public async Task<bool> Exception()
{
try
{
await Task.Delay(delayValue, cancellationToken);
}
catch (OperationCanceledException)
{
// token triggered cancellation
return true;
}
return false;
}
[Benchmark]
public async Task<bool> ContinueWith()
{
var task = await Task.Delay(delayValue, cancellationToken).ContinueWith(t => t);
if (task.IsCanceled)
{
// token triggered cancellation
return true;
}
return false;
}
}
也就是说,您可以通过避免跳转到线程池来继续进行,从而更快:
[Benchmark]
public async Task<bool> ContinueWithSynchronously()
{
var task = await Task.Delay(delayValue, cancellationToken).ContinueWith(t => t, TaskContinuationOptions.ExecuteSynchronously);
if (task.IsCanceled)
{
// token triggered cancellation
return true;
}
return false;
}
NoAsync | 397.7ns | 5.290ns | 4.948ns | 0.0281 |-|-| 148 B|
编辑:我需要花一些时间在最后一个基准测试上,因为我对它分配的内存与异步版本一样多感到非常惊讶。我想知道编译器是否已经在幕后进行了优化(有人说要在.net core上添加该功能,但我很惊讶它已经被移植到.net framework),或者BenchmarkDotNet可能正在进行某些操作。您是否可以改为使用Wait?显示a以帮助更好地理解问题。@Nkosi问题不是关于在哪里可以使用或不能使用
wait
。这是关于如何处理通过取消令牌取消的任务。虽然提供了最低限度的描述,但我将添加代码片段以使其非常清晰。谢谢。@SeanFeldman-只有当它能够被复制、粘贴和运行时,它才算是一个好东西。@Darem第二个代码段编译并运行得很好。返回的是一项任务,而不是一片空白。@SeanFeldman抱歉,我看错了<代码>任务继续选项。同步执行是一个非常好的优化,谢谢。
[Benchmark]
public async Task<bool> ContinueWithSynchronously()
{
var task = await Task.Delay(delayValue, cancellationToken).ContinueWith(t => t, TaskContinuationOptions.ExecuteSynchronously);
if (task.IsCanceled)
{
// token triggered cancellation
return true;
}
return false;
}
ContinueWithSynchronously | 505.2 ns | 2.581 ns | 2.414 ns | 0.0277 | - | - | 148 B |
[Benchmark]
public Task<bool> NoAsync()
{
return Task.Delay(delayValue, cancellationToken).ContinueWith(t => t.IsCanceled, TaskContinuationOptions.ExecuteSynchronously);
}
NoAsync | 397.7 ns | 5.290 ns | 4.948 ns | 0.0281 | - | - | 148 B |