I want to use CancellationTokenSource stop the Task. My tests as follow:
Test 1 : Using Cancel() stopped the task sucessfully.
Test 2 : Using CancelAfter() did not stop the task, Why?
The task action is:
static Action testFun = () => {
Thread.Sleep(4000); // or other a long time operation
Console.WriteLine("Action is end");
};
The test1 code:
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
//Register the cancel action
token.Register(() =>
{
Console.WriteLine("Task is canceled");
});
Task task = new Task(testFun, token);
task.Start();
source.Cancel();
Console.ReadLine();
Output is:
Task is canceled
Test2 Code:
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
//Register the cancel action
token.Register(() =>
{
Console.WriteLine("Task is canceled");
});
Task task = new Task(testFun, token);
task.Start();
source.CancelAfter(100); // the time 100ms < the task 4000ms
Console.ReadLine();
Output is:
Task is canceled
Action is end
My question is why task is still running when CancelAfter()
is invoked on CancellationTokenSource
How do I revise the test2 ? Thank you.
The
CancelationToken
is kind of a misleading. The problem is that ifTask
is already started it cannot be stopped unless you explicitly checkCancelationToken
, for exampleCancellationToken.ThrowIfCancellationRequested
. The purpose of theCancelationToken
is to prevent theTask
to start while it is still scheduled.That is difference in your example, with
Cancel
you cancel the task while it is still scheduled but withCancelAfter
task is already started and there is no way to stop it anymore.https://blog.stephencleary.com/2015/01/a-tour-of-task-part-7-continuations.html
Part explaining the continuation of the
Task
but it is the same also with theTask
itself, continuation will schedule new task anyway.Cancel()
succeeds in cancelling that task, because task had no chance to even start. When task is scheduled and it about to run - there is a check if its not already cancelled. You can verify it by modifyingtestFun
like this:And observe that when you use
Cancel
- there is no "start" output to console, so task has not been started.When you introduce a delay with
CancelAfter
, or by just doing:Task has a chance to start, and after it has been started - cancelling token has no effect, because nothing in the body of
testFunc
checks if token has been cancelled. It's not possible for task to be magically cancelled in the middle of execution without cooperation from that task code.Cooperation can look for example like this (though using
Task.Delay
in this way is not usually recommended):Then when you start task - you pass cancellation token there:
And now there is cooperation -
Task.Delay
will notice that token is cancelled and will cancelTask.Delay
operation, which in turn will cancel your task (by throwingOperationCanceledException
).Same can be done with async\await:
The core problem is the Task won't stop once it is started.
It must be check the IsCancellationRequested.
There is one way to solve this problem:
when task is running , use Thread.CurrentThread to get the thread, and create a new Task to listen the running task's CancellationToken. In the listening function:listening the status of cancel, call thread.Abort() if cancel event is ture. Use Abort() is a unsafe way,but it can stop the task.
The code as follow: