简体   繁体   中英

Canceling long running task on condition

I'm running cpu-bound task in asp.net mvc application. Some users can "subscribe" to this task and they should be notified of completion. But when task have no subscribers it must be cancelled. The task start via ajax request and cancel when .abort() method is called. In controller I have CancellationToken as parameter which determines cancellation.

The problem is that when one of subscribers calls abort (unsubscribe) the linked token cancels task despite other users are waiting for the result. How can I cancel CancellationToken after checking some condition? I can't check IsCancellationRequested prop after every loop iteration because I'm wrapping non-async method.

Users are notified with SignalR after task completion. I've tried to implement ConcurrentDictionary to check before cancelling whether task has subscribers or not.

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
            }

        }

I need something like this, but can't come up with any (not ugly)ideas:

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
            }

        }

You will have to maintain a thread safe subscriber count (most likely using lock ), and only cancel the token when it's 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
       }
   }
}

Note : Obviously this is not a complete solution and leaves a lot to the imagination.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM