正在处理CancellationTokenRegistrations
本文关键字:CancellationTokenRegistrations 处理 | 更新日期: 2023-09-27 18:19:55
有争议的例子,但假设我在异步方法中有以下内容:
var cts = new CancellationTokenSource();
cts.CancelAfter(2000);
cts.Token.Register(Callback);
SomethingThatMightThrow();
await Task.Delay(10000, cts.Token);
只要几秒钟后调用回调,这就可以正常工作。然而,我想在Task.Delay
之后处理注册,所以假设我进行了以下修改:
var cts = new CancellationTokenSource();
cts.CancelAfter(2000);
using (cts.Token.Register(Callback))
{
SomethingThatMightThrow();
await Task.Delay(10000, cts.Token);
}
在这种情况下,回调不是被调用的,可能是因为await Task.Delay...
的异常导致注册在被调用之前被释放。
确保Callback
在取消时被调用,并且始终处理注册的最佳方法是什么?
我确实想到了以下几点,但我不确定它有多强大:
var cts = new CancellationTokenSource();
cts.CancelAfter(2000);
var ctr = cts.Token.Register(Callback);
try
{
SomethingThatMightThrow();
await Task.Delay(10000, cts.Token);
}
finally
{
if (!cts.Token.IsCancellationRequested)
ctr.Dispose();
}
CancellationToken.Register
通常用于将新的CancellationToken
系统与使用其他类型通知进行取消的旧系统进行互操作。它不打算用作通用的"取消回调"。
如果您想在操作被取消时以某种方式做出响应,那么您只需捕获适当的异常:
using (var cts = new CancellationTokenSource())
{
cts.CancelAfter(2000);
SomethingThatMightThrow();
try
{
await Task.Delay(10000, cts.Token);
}
catch (OperationCanceledException)
{
Callback();
}
}
与其说该方法可能不会抛出异常,不如说它可能不会像我想的那样扔得那么快。我注意到一些例如,对于Azure SDK异步方法响应令牌上发出的取消信号
根据您的评论,您可以选择创建自己的计时器,以根据您的标准明确指定方法"运行时间过长"的时间。为此,您可以使用Task.WhenAny
:
using (var cts = new CancellationTokenSource())
{
try
{
var cancellationDelayTask = Task.Delay(2000, cts.Token);
var taskThatMightThrow = SomethingThatMightThrowAsync(cts.Token);
if ((await Task.WhenAny(taskThatMightThrow, cancellationDelayTask))
== cancellationDelayTask)
{
// Task.Delay "timeout" finished first.
}
}
catch (OperationCanceledException)
{
Callback();
}
}