是否有必要在处置前取消cancellationtoken
is it necessary to cancel cancellationtoken before disposal
我大部分时间都使用 CancellationTokenSource
来超时。为避免忘记 dispose
,当我完成 CancellationTokenSource
时,我使用了 using
语句。但是在 using
语句结束之前,我总是在做一个 CancellationTokenSource.Cancel()
.
未使用过的注销是否需要注销CancellationTokenSource
后才能处理?
这是我执行此操作的代码示例。
using (CancellationTokenSource TokenSource = new CancellationTokenSource(nTimeout * 1000))
{
for (int i = 0; i < nLoop; i++)
{
if (TokenSource.Token.IsCancellationRequested)
{
bSuccess = false;
break;
}
await Task.Delay(cDelay);
// do some work
}
TokenSource.Cancel();
}
在处理 CancellationTokenSource
之后,基于此来源的令牌可能会抛出 ObjectDisposedException
,因此您不应在处理该来源后使用 CancellationTokenSource.Token
。幸运的是,我没有在您的代码中看到这种情况。
当您取消 CancellationTokenSource
时,它会更改状态并通知已为令牌注册的回调。但是,当您的代码即将处理 CancellationTokenSource
时,您已经完成了对令牌的使用,无需取消它。
因此,在您的情况下,没有必要在处理之前取消 CancellationTokenSource
。但是,您的用例有些特殊。当你有一个后台任务时,你应该在处理源之前等待任务完成(如我开头段落中所述):
using (var cts = new CancellationTokenSource()) {
var task = Task.Run(() => DoSomething(cts.Token));
// Cancel cts or let it cancel itself based on a timeout.
// Then wait for the task to end.
await task;
}
对于“CancellationTokenSource销毁前是否需要注销?”的问题答案是否定的,没有必要。
但取消与否决定了孤儿令牌的最终状态。
作为一般规则,在处置其来源后不应使用孤立令牌。但是,如果您对尝试使用它会发生什么感到好奇,那么它应该在某种程度上起作用。例如检查这个测试:
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Test48538934(bool canceledBeforeDispose)
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
Assert.False(cts.IsCancellationRequested);
Assert.False(ct.IsCancellationRequested);
if (canceledBeforeDispose)
cts.Cancel();
Assert.Equal(canceledBeforeDispose, cts.IsCancellationRequested);
Assert.Equal(canceledBeforeDispose, ct.IsCancellationRequested);
cts.Dispose();
Assert.Throws<ObjectDisposedException>(() => cts.Token);
Assert.Equal(canceledBeforeDispose, cts.IsCancellationRequested);
Assert.Equal(canceledBeforeDispose, ct.IsCancellationRequested);
}
因此,如果令牌在异步调用管道中的某处意外丢失,可能需要在处理它之前取消源。
我大部分时间都使用 CancellationTokenSource
来超时。为避免忘记 dispose
,当我完成 CancellationTokenSource
时,我使用了 using
语句。但是在 using
语句结束之前,我总是在做一个 CancellationTokenSource.Cancel()
.
未使用过的注销是否需要注销CancellationTokenSource
后才能处理?
这是我执行此操作的代码示例。
using (CancellationTokenSource TokenSource = new CancellationTokenSource(nTimeout * 1000))
{
for (int i = 0; i < nLoop; i++)
{
if (TokenSource.Token.IsCancellationRequested)
{
bSuccess = false;
break;
}
await Task.Delay(cDelay);
// do some work
}
TokenSource.Cancel();
}
在处理 CancellationTokenSource
之后,基于此来源的令牌可能会抛出 ObjectDisposedException
,因此您不应在处理该来源后使用 CancellationTokenSource.Token
。幸运的是,我没有在您的代码中看到这种情况。
当您取消 CancellationTokenSource
时,它会更改状态并通知已为令牌注册的回调。但是,当您的代码即将处理 CancellationTokenSource
时,您已经完成了对令牌的使用,无需取消它。
因此,在您的情况下,没有必要在处理之前取消 CancellationTokenSource
。但是,您的用例有些特殊。当你有一个后台任务时,你应该在处理源之前等待任务完成(如我开头段落中所述):
using (var cts = new CancellationTokenSource()) {
var task = Task.Run(() => DoSomething(cts.Token));
// Cancel cts or let it cancel itself based on a timeout.
// Then wait for the task to end.
await task;
}
对于“CancellationTokenSource销毁前是否需要注销?”的问题答案是否定的,没有必要。 但取消与否决定了孤儿令牌的最终状态。
作为一般规则,在处置其来源后不应使用孤立令牌。但是,如果您对尝试使用它会发生什么感到好奇,那么它应该在某种程度上起作用。例如检查这个测试:
[Theory]
[InlineData(false)]
[InlineData(true)]
public void Test48538934(bool canceledBeforeDispose)
{
var cts = new CancellationTokenSource();
var ct = cts.Token;
Assert.False(cts.IsCancellationRequested);
Assert.False(ct.IsCancellationRequested);
if (canceledBeforeDispose)
cts.Cancel();
Assert.Equal(canceledBeforeDispose, cts.IsCancellationRequested);
Assert.Equal(canceledBeforeDispose, ct.IsCancellationRequested);
cts.Dispose();
Assert.Throws<ObjectDisposedException>(() => cts.Token);
Assert.Equal(canceledBeforeDispose, cts.IsCancellationRequested);
Assert.Equal(canceledBeforeDispose, ct.IsCancellationRequested);
}
因此,如果令牌在异步调用管道中的某处意外丢失,可能需要在处理它之前取消源。