Search code examples
c#multithreadingtask-parallel-librarytask

Cannot cancel a task via CancellationTokenSource.Cancel()


I am having trouble stopping a Task. Firstly, I just started using Tasks. Previously, I had been using delegate.BeginInvoke() to run things in background, but this time I need to stop background execution, if required. So I switched to Tasks. This is my code:

CancellationTokenSource token = new CancellationTokenSource();
Task posting = Task.Factory.StartNew(() => DoPosting(docs, session), token.Token);
HttpRuntime.Cache[session.ToString() + "_token"] = token;
HttpRuntime.Cache[session.ToString() + "_task"] = posting;

This is ASP.NET MVC, so I store long-lasting things in HttpRuntime.Cache. User can cancel operation via this action:

public JsonResult StopPosting(string session)
{
    CancellationTokenSource token = (CancellationTokenSource)HttpRuntime.Cache.Get(session.ToString() + "_token");
    Task posting = (Task)HttpRuntime.Cache[session.ToString() + "_task"];
    token.Cancel();

    return Json(new { Message = "Stopped!" });
}

Now, when this action gets hit for the first time, nothing happens. Then i request cancellation for the second time. Now, token.IsCancellationRequested says "true", so token.Cancel() must have done something. But posting.Status is still "Running". It will stay that way until it completes and then it is "RunToCompletion".

So, I requested cancellation of a Task, but it didt got cancelled.

Maybe I am doing something wrong or I am missing something obvious, but I just cant see/understand why it wont cancel.

Maybe somebody can shed some light?

Regards.


Solution

  • Cancelling the token won't immediately result in the task's delegate stopping execution right where it is. Supporting that (as is done via Abort through threads) can cause all sorts of badness, ranging from objects not being cleaned up properly, invariants being violated due to execution stopping part way through an operation that ought to be logically observed to be atomic, etc.

    What you need to do is have the actual function being executed look at the CancellationToken and periodically verify that it hasn't been cancelled. In addition to passing the token to StartNew you need to pass it to the DoPosting method. That method then needs to periodically call token.ThrowIfCancellationRequested(); at places in the method where it would be appropriate to stop if cancellation was indeed requested.

    See How do I cancel non-cancelable async operations? for further reading.