I thought that the CancellationToken/CancellationTokenSource system worked a bit like a C++ volatile bool bFlagCancelled
, meaning that cancellation is voluntary on the part of the task, and relies on the task itself to check from time to time whether it is cancelled and throw an exception, either explicitly or by calling ThrowIfCancellationRequested()
.
But if I call Cancel immediately after StartNew()
, the task stops, and calling Wait()
throws a TaskCanceledException
.
For example, for this code:
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
Task task = Task.Factory.StartNew(
() =>
{
Console.WriteLine("start sleep");
Thread.Sleep(1000);
Console.WriteLine("sleep ended");
}
, token);
// Thread.Sleep(1);
source.Cancel();
Console.WriteLine("start wait");
task.Wait();
Console.WriteLine("wait ended");
I get this output:
start wait Exception: System.AggregateException: One or more errors occurred. ---> System.Threading.Tasks.TaskCanceledException: A task was canceled.
But if I uncomment // Thread.Sleep(1);
, then the behavior changes and I get this output:
start sleep start wait sleep ended wait ended
Now I thought maybe this is because task.Start()
hasn't been called yet, but as far as I understand it, StartNew()
calls task.Start()
before returning - that's why it doesn't have the synchronization cost and is recommended vs creating a new Task
and calling task.Start()
yourself.
So then this means in some situations, a task will cancel spontaneously, even without checking the cancellation token. Is this the only situation where this happens, or are there more scenarios where this happens?
Task.Factory.StartNew
schedules the task before returning, but it doesn't mean the task actually started to execute. So there is still room to cancel the task, if the TaskScheduler
accepts it (internally, the TaskScheduler.TryDequeue
method is called. The task can be marked as cancelled if it returns true).