0

Lets say I have something like this:

private CancellationTokenSource myToken;

public void MyMyMethod()
{
  myToken = new CancellationTokenSource();
  var task = Task.Factory.StartNew(() => DoIt(myToken.Token), myToken.Token);

  Thread.Sleep(100);
  myToken.Cancel();
}

public void MyOtherMethod()
{
  myToken.Cancel();
}

private void DoIt(CancellationToken token)
{
    token.ThrowIfCancellationRequested();
    try
    {
       for (int i = 0; i < 1000000; i++)
       {
         Console.WriteLine(i);
        }
    }
    catch (Exception ex)
    {
       string s = "";
    }
}

If I call myToken.Cancel will it stop the task in the DoIt method abruptly or do I have to pass in the token to DoIt and call myToken.ThrowIfCancellationRequested() so that when the Cancel is called it will throw the exception and stop abruptly?

Can I not do this without passing in the token to the task method?

Or do I have to monitor token.IsCancellationRequested in the DoIt method?

Jon
  • 38,814
  • 81
  • 233
  • 382

2 Answers2

2

By reading from here http://social.msdn.microsoft.com/Forums/da-DK/parallelextensions/thread/9f88132a-f8bd-4885-ab63-645d7b6c2127 it seems that the token is used to cancel the task BEFORE the task is "really" started, but after it has been queued.

It's more a way to cancel a task that's scheduled to occur, but not started yet. Once the task is running, the only way to cancel it is cooperatively via your own checking within the method. Without this, you'd have to always start the task, then check it internally, which would add a lot of extra, unnecessary overhead

You can even read it from Cancellation token in Task constructor: why?

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • As I guessed then, you have to monitor the IsCancellationRequested property in the method. That could be quite tricky if you have various loops etc in the method. I was hoping there was a way to abort the task. – Jon Mar 13 '12 at 13:07
  • Why would it be tricky? `ThrowIfCancellationRequested` throws `OperationCanceledException`, which does a pretty good job of stopping all your loops :) – anton.burger Mar 13 '12 at 13:31
  • @Anton He meant that he has to check every few lines if the task has been cancelled... He hoped for an exception to be magically injected in the task to stop the thread... not knowing that this is exactly what `Thread.Abort` does, and that it is *evil* :-) – xanatos Mar 13 '12 at 13:32
  • So I need to add checks in my method to return if cancelled but where is the exception thrown? – Jon Mar 13 '12 at 13:50
  • @Jon Read here http://msdn.microsoft.com/en-us/library/dd997415.aspx the section named `Exceptions That Indicate Cooperative Cancellation ` – xanatos Mar 13 '12 at 13:55
  • @xanatos See my modified question with some simple code. The WriteLine always seems to be called – Jon Mar 13 '12 at 14:05
  • @Jon Try adding a `Thread.Sleep(5000);` before `token.ThrowIfCancellationRequested();` – xanatos Mar 13 '12 at 14:07
  • @xanatos No difference! I'm getting confused as to whether I can just put a ThrowIfCancellationRequested(); at the top of the method or do I have to do it every few lines? Do I also need to check the IsCancellationRequested property as well? – Jon Mar 13 '12 at 14:12
  • @Jon A) It is `myToken.Cancel();` and not `tokensource.Cancel();` and B) you should put the `token.ThrowIfCancellationRequested();` IN the `for` cycle and C) you should probably `task.Wait()` after the `Cancel` or at least to other lengthy operations (otherwise having 2 threads working (the main and the `Task`) is useless. – xanatos Mar 13 '12 at 14:19
  • You're correct about the typo, I added a if (IsCancellationRequested) token.ThrowIfCancellationRequested(); in the for. I can't have the wait as I want the GUI to still be responsive – Jon Mar 13 '12 at 14:27
  • Think I will just have to check the IsCancellationRequested property every few lines and just return out of the method gracefully – Jon Mar 13 '12 at 14:29
  • @Jon Ok. I have tested the code and it works correctly here... And you have to put the `token.ThrowIfCancellationRequest` every few lines :-) Ideally you should put it before and after every long operation. – xanatos Mar 13 '12 at 14:29
2

do I have to pass in the token to DoIt and call myToken.ThrowIfCancellationRequested()

Yes, you do. TPL cancellation is cooperative, which means your thread function must periodically check if cancellation has been requested and if so, terminate itself.

The alternative would have been to call Thread.Abort() which is generally not a good idea.

Nick Butler
  • 24,045
  • 4
  • 49
  • 70
  • So for example I would call ThrowIfCancellationRequested which I assume just sets an internal property. I then terminate my task nicely. I then get an exception thrown somewhere? – Jon Mar 13 '12 at 13:12
  • `ThrowIfCancellationRequested()` throws an [OperationCancelledException](http://msdn.microsoft.com/en-us/library/system.operationcanceledexception.aspx) which will terminate your thread function. The TPL recognises this and marks the `Task` as `Cancelled`. – Nick Butler Mar 13 '12 at 13:51
  • Maybe its the way I'm testing but the thread is still writing to the console. See my updated question with added code. – Jon Mar 13 '12 at 13:57
  • 1
    You need to call `ThrowIfCancellationRequested()` **inside** your loop. You also need to re-throw the `OperationCancelledException` in a `catch` block. – Nick Butler Mar 13 '12 at 14:24
  • or you can use an if stmt - 'if (myToken.IsCancellationRequested)' from a specific spot in your loop to specifically come out of the process in a more graceful fashion and possibly finish or clean up what you were doing. Of course that would require that you are getting back to that place in your loop at reasonable intervals. – Robb Sadler Sep 15 '14 at 14:42