Search code examples
c#multithreadingasynchronoustaskcancellationtokensource

c# async task cancellation


I have some problems getting a grasp on tasks and cancellation tokens. I've made a program that looks like this:

static void Main(string[] args)
{

    CancellationTokenSource token = new CancellationTokenSource();
    Stopwatch stop = new Stopwatch();
    stop.Start();

    for (int i = 0; i < 5; i++)
    {
        //Thread.Sleep(1000);
        Task.Factory.StartNew(() => myLongTask(token.Token, (i + 1) * 1000));
    }
    while (true)
    {
        Thread.SpinWait(1000);
        if (stop.ElapsedMilliseconds > 3000)
        {
            token.Cancel();
        }
    }
}

public static void myLongTask(CancellationToken token, int time)
{
    if (token.IsCancellationRequested)
    {
        Console.WriteLine("Cancelled");
        return;
    }
    var sw = Stopwatch.StartNew();
    Console.WriteLine($"Task {time / 1000} started");
    while (sw.ElapsedMilliseconds < time)
        Thread.SpinWait(1000);
    Console.WriteLine($"Task {time / 1000} ended");

}

I am running 5 tasks simultaneously (although when I don't include Thread.Sleep() the for loop seems to run before tasks are even started?). None of the tasks get ever cancelled when I run the program. Also what is bugging me is...what task am I really cancelling when calling token.Cancel()? How can I choose which of the 5 tasks will I kill? I can define each task by it's variable, but I can't access its CancellationRequested property since it is triggered with CancellationToken. Would I need 5 different tokens then?


Solution

  • None of the tasks get ever cancelled when I run the program.

    That's because you're only ever checking the cancellation token at the start of the task. Once it's got past that first token.IsCancellationRequested check, cancelling the token does nothing. If you move your check into your loop, like this:

    while (sw.ElapsedMilliseconds < time)
    {
        if (token.IsCancellationRequested)
        {
            Console.WriteLine("Cancelled");
            return;
        }
        Thread.SpinWait(1000);
    }
    

    ... then you'll see the tasks react appropriately.

    Also what is bugging me is...what task am I really cancelling when calling token.Cancel()?

    You're not cancelling a task - you're cancelling the cancellation token. Any task that observes that cancellation token will be cancelled (or complete, or whatever action it takes) but there's no direct correlation between a task and a token.

    When we talk about "cancelling a task" we really mean "cancelling a token which we believe the task is observing".