Enr*_*one 6 c# task-parallel-library async-await cancellationtokensource .net-core
考虑这样一个场景,您需要完成一些异步工作,并且可以在即发即弃模式下运行它。这种异步工作能够侦听取消,因此您向它传递取消令牌以便能够取消它。
在给定的时刻,我们可以决定请求取消正在进行的活动,方法是使用我们从中获取取消令牌的取消令牌源对象。
因为取消令牌源实现了IDisposable,所以我们应该尽可能地调用它的Dispose方法。这个问题的重点是确定您何时完成给定的取消令牌源。
假设您决定通过调用Cancel取消令牌源上的方法来取消正在进行的工作:在调用之前是否需要等待正在进行的操作完成Dispose?
换句话说,我应该这样做:
class Program
{
static void Main(string[] args)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
DoSomeAsyncWork(token); // starts the asynchronous work in a fire and forget manner
// do some other stuff here
cts.Cancel();
cts.Dispose(); // I call Dispose immediately after cancelling without waiting for the completion of ongoing work listening to the cancellation requests via the token
// do some other stuff here not involving the cancellation token source because it's disposed
}
async static Task DoSomeAsyncWork(CancellationToken token)
{
await Task.Delay(5000, token).ConfigureAwait(false);
}
}
Run Code Online (Sandbox Code Playgroud)
或者这样:
class Program
{
static async Task Main(string[] args)
{
var cts = new CancellationTokenSource();
var token = cts.Token;
var task = DoSomeAsyncWork(token); // starts the asynchronous work in a fire and forget manner
// do some other stuff here
cts.Cancel();
try
{
await task.ConfigureAwait(false);
}
catch(OperationCanceledException)
{
// this exception is raised by design by the cancellation
}
catch (Exception)
{
// an error has occurred in the asynchronous work before cancellation was requested
}
cts.Dispose(); // I call Dispose only when I'm sure that the ongoing work has completed
// do some other stuff here not involving the cancellation token source because it's disposed
}
async static Task DoSomeAsyncWork(CancellationToken token)
{
await Task.Delay(5000, token).ConfigureAwait(false);
}
}
Run Code Online (Sandbox Code Playgroud)
其他详细信息:我所指的代码是在 ASP.NET core 2.2 Web 应用程序中编写的,这里我使用控制台应用程序场景只是为了简化我的示例。
我在 stackoverflow 上发现了类似的问题,要求处理取消令牌源对象。一些答案表明,在某些情况下,并不真正需要处理这个对象。
我对整个IDisposable主题的方法是,我总是倾向于遵守一个类的公开契约,换句话说,如果一个对象声称是一次性的,我更喜欢Dispose在我完成后总是调用它。我不喜欢根据类的实现细节来猜测是否真的需要调用 dispose 的想法,这些实现细节可能会在未来版本中以未记录的方式更改。
The*_*ias 11
CancellationTokenSource为了确保与“即发即弃”关联的CTS ( )Task最终被处置,您应该将延续附加到任务,并从延续内部处置 CTS。但这会产生一个问题,因为Cancel当对象正在处理时另一个线程可以调用该方法,并且根据文档,该Dispose方法不是线程安全的:
的所有公共和受保护成员
CancellationTokenSource都是线程安全的,并且可以在多个线程中同时使用,但 除外Dispose(),它只能在CancellationTokenSource对象上的所有其他操作完成后才能使用。
因此,在没有同步的情况下同时从两个不同的线程调用Cancel和Dispose不是一种选择。这只剩下一个选项可用:在 CTS 类的所有公共成员周围添加一层同步。但这并不是一个令人愉快的选择,原因如下:
因此,我的建议是采取替代方案,即仅在无法等待其关联任务完成的情况下,不处理 CTS。换句话说,如果无法将使用 CTS 的代码包含在语句中using,则只需让垃圾收集器回收保留的资源即可。这意味着您必须遵守文档的这一部分:
在释放对 的最后一个引用之前,请务必调用 Dispose
CancellationTokenSource。CancellationTokenSource否则,在垃圾收集器调用该对象的方法之前,它正在使用的资源不会被释放Finalize。
...和这个:
该类
CancellationTokenSource实现该IDisposable接口。CancellationTokenSource.Dispose当您使用完取消令牌源以释放它所持有的任何非托管资源时,您应该确保调用该方法。
如果这让您感觉有点肮脏,那么您并不孤单。Task如果您认为该类也实现了该接口,但不需要IDisposable处置任务实例,您可能会感觉更好。
正确的做法是第二个CancellationTokenSource-在确定任务被取消后将 其丢弃。CancellationToken依赖来自的信息CancellationTokenSource才能正常运行。虽然当前实现的CancellationToken编写方式是,如果创建它的 CTS 被释放,即使不抛出异常,它仍然可以工作,但它可能无法正常运行或始终按预期运行。
| 归档时间: |
|
| 查看次数: |
1289 次 |
| 最近记录: |