gpt4 book ai didi

c# - 处理取消 token 源的正确模式

转载 作者:行者123 更新时间:2023-12-03 14:15:13 24 4
gpt4 key购买 nike

考虑一个场景,您有一些异步工作要完成,您可以在“一劳永逸”模式下运行它。此异步工作能够监听取消,因此您将取消 token 传递给它以便能够取消它。

在给定的时间,我们可以决定请求取消正在进行的事件,方法是使用我们从中获取取消 token 的取消 token 源对象。

因为取消 token 源实现 IDisposable ,我们应该称它为Dispose就我们完成它的方法而言。这个问题的重点是准确地确定 您已经完成了给定的取消 token 源。

假设您决定通过调用 Cancel 来取消正在进行的工作。取消 token 来源的方法:在调用 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);
}
}

或者这样:
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);
}
}

其他详细信息:我所指的代码是在 ASP.NET core 2.2 Web 应用程序中编写的,这里我使用控制台应用程序场景只是为了简化我的示例。

我在 stackoverflow 上发现了类似的问题,要求处理取消 token 源对象。一些答案表明,在某些情况下,并不真正需要处理此对象。

我的整体方法 IDisposable主题是我总是倾向于遵守一个类的暴露契约,换句话说,如果一个对象声称是一次性的,我更喜欢总是调用 Dispose当我完成它时。我不喜欢根据类的实现细节来猜测是否真的需要调用 dispose 的想法,这些实现细节可能会在 future 的版本中以未记录的方式发生变化。

最佳答案

确保 CTS ( CancellationTokenSource ) 与即发即弃 Task 关联最终将被处置,您应该为任务附加一个延续,并从延续内部处置 CTS。但是这会产生一个问题,因为另一个线程可以调用 Cancel处理对象时的方法,并根据 documentation Dispose方法不是线程安全的:

All public and protected members of CancellationTokenSource are thread-safe and may be used concurrently from multiple threads, with the exception of Dispose(), which must only be used when all other operations on the CancellationTokenSource object have completed.



所以调用 CancelDispose来自两个不同的线程同时没有同步不是一种选择。这只剩下一个选项可用:围绕 CTS 类的所有公共(public)成员添加一层同步。不过,这不是一个令人愉快的选择,原因如下:
  • 您必须编写线程安全的包装类(编写代码)
  • 每次启动可取消的即发即弃任务时都必须使用它(编写更多代码)
  • 导致同步性能损失
  • 导致附加延续的性能损失
  • 必须维护一个变得更加复杂和更容易出错的系统
  • 必须解决哲学问题,为什么类一开始就没有设计为线程安全的

  • 因此,我的建议是替代方案,即仅在无法等待其相关任务完成的情况下不处理 CTS。换句话说,如果无法将使用 CTS 的代码包含在 using 中声明,只是让垃圾收集器来做保留资源的回收。这意味着你必须不服从 this部分文档:

    Always call Dispose before you release your last reference to the CancellationTokenSource. Otherwise, the resources it is using will not be freed until the garbage collector calls the CancellationTokenSource object's Finalize method.



    ...和 ​​ this :

    The CancellationTokenSource class implements the IDisposable interface. You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.



    如果这让你觉得有点脏,你并不孤单。如果您认为 Task 类实现 IDisposable接口(interface)也是,但处理任务实例 is not required .

    关于c# - 处理取消 token 源的正确模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61359443/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com