Using a CancellationToken to cancel a task without explicitly checking within the task?

前端 未结 3 1035
遇见更好的自我
遇见更好的自我 2020-12-21 07:31

Background:

I have a web application which kicks off long running (and stateless) tasks:

var          


        
相关标签:
3条回答
  • 2020-12-21 07:56

    If you actually just have a bunch of method calls you are calling one after the other, you can implement a method runner that runs them in sequence and checks in between for the cancellation.

    Something like this:

    public static void WorkUntilFinishedOrCancelled(CancellationToken token, params Action[] work)
    {
        foreach (var workItem in work)
        {
            token.ThrowIfCancellationRequested();
            workItem();
        }
    }
    

    You could use it like this:

    async Task<Bar> DoWork(Foo foo)
    {
        WorkUntilFinishedOrCancelled([YourCancellationToken], DoStuff1, DoStuff2, DoStuff3, ...);        
    }
    

    This would essentially do what you want.

    0 讨论(0)
  • 2020-12-21 08:01

    If you are OK with the implications of Thread.Abort (disposables not disposed, locks not released, application state corrupted), then here is how you could implement non-cooperative cancellation by aborting the task's dedicated thread.

    private static Task<TResult> RunAbortable<TResult>(Func<TResult> function,
        CancellationToken cancellationToken)
    {
        var tcs = new TaskCompletionSource<TResult>();
        var thread = new Thread(() =>
        {
            try
            {
                TResult result;
                using (cancellationToken.Register(Thread.CurrentThread.Abort))
                {
                    result = function();
                }
                tcs.SetResult(result);
            }
            catch (ThreadAbortException)
            {
                tcs.TrySetCanceled();
            }
            catch (Exception ex)
            {
                tcs.TrySetException(ex);
            }
        });
        thread.IsBackground = true;
        thread.Start();
        return tcs.Task;
    }
    

    Usage example:

    var cts = new CancellationTokenSource();
    var task = RunAbortable(() => DoWork(foo), cts.Token);
    task.Wait();
    
    0 讨论(0)
  • 2020-12-21 08:02

    Is it possible to automatically throw an exception in the task when it has been canceled, and if not, are there any good alternatives (patterns, etc.) to reduce polluting the code with CancellationToken.ThrowIfCancellation()?

    No, and no. All cancellation is cooperative. The best way to cancel code is to have the code respond to a cancellation request. This is the only good pattern.

    I think I can accomplish what I want creating an explicit Thread

    Not really.

    At this point, the question is "how do I cancel uncancelable code?" And the answer to that depends on how stable you want your system to be:

    1. Run the code in a separate Thread and Abort the thread when it is no longer necessary. This is the easiest to implement but the most dangerous in terms of application instability. To put it bluntly, if you ever call Abort anywhere in your app, you should regularly restart that app, in addition to standard practices like heartbeat/smoketest checks.
    2. Run the code in a separate AppDomain and Unload that AppDomain when it is no longer necessary. This is harder to implement (you have to use remoting), and isn't an option in the Core world. And it turns out that AppDomains don't even protect the containing application like they were supposed to, so any apps using this technique also need to be regularly restarted.
    3. Run the code in a separate Process and Kill that process when it is no longer necessary. This is the most complex to implement, since you'll also need to implement some form of inter-process communication. But it is the only reliable solution to cancel uncancelable code.

    If you discard the unstable solutions (1) and (2), then the only remaining solution (3) is a ton of work - way, way more than making the code cancelable.

    TL;DR: Just use the cancellation APIs the way they were designed to be used. That is the simplest and most effective solution.

    0 讨论(0)
提交回复
热议问题