【问题标题】:Adding abort all tasks using a single cancellation token使用单个取消令牌添加中止所有任务
【发布时间】:2017-08-12 17:21:49
【问题描述】:

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

几个问题:

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

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

  3. 是否有一种已经实现的方法来监视所有当前正在运行的任务,所以当发送取消时,我会确保所有任务都已结束,然后才允许任何新任务?我是用信号量做的,但它看起来很糟糕。..

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

【问题讨论】:

    标签: c# .net multithreading threadpool cancellationtokensource


    【解决方案1】:
    1. 任务没有收到任何取消。它只是令牌的状态变化。任务必须观察令牌,如果有取消请求,那么任务可能会在可能的情况下取消操作。

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

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

    作为一个示例如何实现这样的取消,您在这里寻找一个非常简单的示例

    原创服务

    public interface IFooService
    {
        Task DoAsync( CancellationToken cancellationToken );
    }
    

    以及将处理取消的那个

    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;
        }
    }
    

    【讨论】:

    • 首先,谢谢! 1. 有没有办法取消已经取消的TokenSource? 3. 有没有办法至少监控当前正在运行的任务?所以我可以看到所有的任务都结束了
    • @O.San 为什么要“取消”?您是否尝试使用 CancellationTokenSource 来模拟其他一些功能?
    • @O.San 至于监控,你不需要。 Task.WhenAll 仅在所有任务完成时完成。
    • @O.San 你看到_createdtasks了吗?那就是服务包装器将存储由它创建的所有任务的地方,也是当您调用CancelAsync 时它将等待的那些。 CancellationTokenSource 无法处理“取消取消”。
    • 谢谢大家,帮了大忙
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多