使用单个取消令牌添加中止所有任务

O. *_*San 0 .net c# multithreading threadpool cancellationtokensource

我收到了一项可以同时执行多项任务的服务。我被分配添加一个中止选项,该选项应该结束所有当前正在运行的任务。因为系统已经实现,所以将 CancellationTokenSource 传递给每个任务是有问题的,所以我所做的是创建一个所有任务都会侦听取消的单个令牌。当中止接收时,该令牌将发送取消,然后将创建一个新的 CancellationTokenSource 实例。我无法验证所有任务,直到中止之前结束在 启动 CancellationTokenSource 的新实例

几个问题:

  1. 起初我以为 TokenSource.Cancel() 只是一个发送的事件,但它似乎被卡在“高位”,所以任何具有相同令牌的新任务也会收到取消,是真的吗?

  2. 理想情况下,每个任务都有自己的取消令牌(对吗?),问题是谁需要持有该令牌?谁调用了该任务或任务本身?因为一旦发送取消,我将需要永远保留该令牌,因为我无法判断任务何时到达退出点。

  3. 是否有一种已经实现的方法来监视所有当前正在运行的任务,以便在发送取消时我将确保在允许任何新任务之前所有任务都已结束?我使用信号量做到了这一点,但它看起来像是一个糟糕的解决方案。

对于新手问题,我很抱歉,我已经实现了我需要的内容并且它有效,但质量远低于标准,我希望以正确的方式改进它。

Sir*_*ufo 5

  1. 该任务没有收到任何取消。它只是令牌的状态变化。任务必须监视令牌,如果有取消请求,则任务可能会取消操作(如果可能)。

  2. 每个任务都有自己的令牌,因为CancellationToken是 astruct并且将按值复制。为了观察令牌(参见 1.),任务必须保留令牌。

  3. 不可以,您必须等待任务,并且不能强制取消任务。通过取消操作,您只需发送取消请求,正在运行的任务可能会取消或成功完成(取决于实现)。只有尚未启动的任务(等待运行)才会被调度程序取消(它们根本不会启动)

作为如何实现此类取消的示例,您正在寻找一个非常简单的示例

原来服务

public interface IFooService
{
    Task DoAsync( CancellationToken cancellationToken );
}
Run Code Online (Sandbox Code Playgroud)

以及将处理取消的人

public interface ICancelableFooService : IFooService
{
    Task CancelAsync();
}

public class CancelableFooService : ICancelableFooService
{
    private readonly IFooService _foo_service;
    private readonly object _sync = new object();
    private List<Task> _createdtasks = new List<Task>();
    private CancellationTokenSource _cts = new CancellationTokenSource();

    public CancelableFooService(IFooService foo_service)
    {
        _foo_service = foo_service;
    }

    public async Task CancelAsync()
    {
        _cts.Cancel();
        var t = Task.WhenAll( _createdtasks );
        try
        {
            await t;
        }
        catch { /* we eat all exceptions here */ }
        lock( _sync )
        {
            _cts = new CancellationTokenSource();
            _createdtasks.Clear();
        }
    }

    public Task DoAsync( CancellationToken cancellationToken )
    {
        lock(_sync)
        {
            var cts = CancellationTokenSource.CreateLinkedTokenSource( _cts.Token, cancellationToken );
            var token = cts.Token;
            var task = _foo_service.DoAsync( token );
            _createdtasks.Add( task );
        }
        return task;
    }
}
Run Code Online (Sandbox Code Playgroud)