【问题标题】:Canceling long running task on condition有条件取消长时间运行的任务
【发布时间】:2019-10-25 15:20:47
【问题描述】:

我在 asp.net mvc 应用程序中运行 cpu-bound 任务。一些用户可以“订阅”此任务,并且应该通知他们完成。但是当任务没有订阅者时,它必须被取消。任务通过 ajax 请求启动,并在调用 .abort() 方法时取消。在控制器中,我将 CancellationToken 作为确定取消的参数。

问题在于,当其中一个订阅者调用 abort(取消订阅)时,链接的令牌会取消任务,尽管其他用户正在等待结果。检查某些条件后如何取消 CancellationToken?我无法在每次循环迭代后检查 IsCancellationRequested 道具,因为我正在包装非异步方法。

任务完成后通过 SignalR 通知用户。我尝试实现 ConcurrentDictionary 在取消任务之前检查是否有订阅者。

private async Task<Diff> CompareAsync(Model modelName, CancellationToken ct)
        {
            try
            {
                return await Task.Factory.StartNew(() =>
                {
                    ct.ThrowIfCancellationRequested();

                        return _someServiceName.CompareLines(modelName.LinesA, modelName.LinesB, ct);


                }, ct, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {

                //do some things
            }

        }

我需要这样的东西,但想不出任何(不丑)的想法:

private async Task<Diff> CompareAsync(Model modelName, CancellationToken ct)
        {
            try
            {
                return await Task.Factory.StartNew(() =>
                {
                    using (var source = new CancellationTokenSource())
                    {

                        if (ct.IsCancellationRequested && CompareService.SharedComparison.TryGetValue(modelName.Hash, out var usersCount) && usersCount < 2)
                        {
                            source.Cancel();

                        }

                        return _someServiceName.CompareLines(modelName.LinesA, modelName.LinesB, source.Token);  
                    }


                }, ct, TaskCreationOptions.LongRunning, TaskScheduler.Default).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {

                //do some things
            }

        }

【问题讨论】:

    标签: c# asp.net-mvc concurrency


    【解决方案1】:

    您必须保持 线程安全 订阅者数量(很可能使用 lock),并且只有在令牌为 0 时才取消令牌。

    private int _subscribers;
    private object _sync = new object();
    
    private AddSubscribers()
    {
       Lock(_sync )
       {
           // do what ever you need to do
           _subscribers++;
       }
    }
    
    private RemoveSubscribers()
    {
       Lock(_sync )
       {
           // do what ever you need to do
           _subscribers--;
           if(_subscribers <= 0)
           {
               // cancel token
           }
       }
    }
    

    注意:显然这不是一个完整的解决方案,还有很多想象空间。

    【讨论】:

    • 感谢您的快速回复。但是取消时检查条件的主要问题。我只能通过 ThrowIfCancellationRequested() 取消任务,这不允许我检查订阅者的数量。我只是想出将令牌保留在会话/缓存中的想法,以便在添加/删除订阅者时可以调用取消方法。但不确定令牌是否会链接在一起
    • @Memskiy 是的,我不太确定我是否理解这里的问题,但是我把它留在这里让其他人知道它对你来说不是一个有效的解决方案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多