Consider the code below
static void Main(string[] args)
{
var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;
Task<string> task = Task.Run(() =>
{
ct.ThrowIfCancellationRequested();
var task2 = ActualAsyncTask();
while (!task2.IsCompleted)
{
var t = DateTime.Now;
while (DateTime.Now - t < TimeSpan.FromSeconds(1))
{
}
ct.ThrowIfCancellationRequested();
}
return task2;
}, ct);
Console.ReadLine();
ts.Cancel();
Console.ReadLine();
}
static async Task<string> ActualAsyncTask()
{
await Task.Delay(1000);
for(int i = 0; i < 100; ++i)
{
var t = DateTime.Now;
while (DateTime.Now - t < TimeSpan.FromSeconds(1))
{
}
Console.WriteLine("tick");
}
return "success";
}
It spawns a task that busy-waits for a cancellation request while an asynchronous method also busy-waits and prints some text into console.
When you let it run for a few seconds and then press enter, the task will throw the cancellation exception. While it's an interesting trick, I don't understand how the async method is able to yield control and make it possible.
Both the anonymous lambda within the task and the asynchronous method report to run on the same worker thread, which means they should be synchronous. In my understanding this setup should not throw the cancellation exception past the first 1 second await, because past that point there are no more awaits to allow the while loop of the anonymous lambda to gain control of the thread flow, yet somehow they seemingly both run in parallel using one thread.
If I remove the await command entirely, the execution becomes as expected - sending a cancellation request no longer interrupts the task. What am I missing?