C# отмена асинхронной задачи

У меня есть некоторые проблемы с пониманием задач и токенов отмены. Я сделал программу, которая выглядит так:

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");

}

Я запускаю 5 задач одновременно (хотя, когда я не включаю Thread.Sleep(), кажется, что цикл for запускается еще до запуска задач?). Ни одна из задач не отменяется, когда я запускаю программу. Также меня беспокоит... какую задачу я действительно отменяю при вызове token.Cancel()? Как я могу выбрать, какую из 5 задач я буду убивать? Я могу определить каждую задачу по ее переменной, но я не могу получить доступ к ее свойству CancellationRequested, так как она запускается с помощью CancellationToken. Нужны ли мне тогда 5 разных токенов?


person Norgul    schedule 28.10.2016    source источник


Ответы (1)


Ни одна из задач не отменяется, когда я запускаю программу.

Это потому, что вы проверяете токен отмены только в начале задачи. После того, как первая проверка token.IsCancellationRequested прошла, отмена маркера ничего не дает. Если вы переместите свою проверку в свой цикл, например:

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

... тогда вы увидите, что задачи реагируют соответствующим образом.

Также меня беспокоит... какую задачу я действительно отменяю при вызове token.Cancel()?

Вы не отменяете задачу — вы отменяете токен отмены. Любая задача, которая обнаруживает этот токен отмены, будет отменена (или завершена, или любое другое действие, которое она предпримет), но между задачей и токеном нет прямой связи.

Когда мы говорим об «отмене задачи», мы на самом деле имеем в виду «отмену токена, который, по нашему мнению, наблюдает задача».

person Jon Skeet    schedule 28.10.2016
comment
Но если у меня 5 разных задач и только одна token.Cancel()...тогда все 5 явно отменяется? Если я хочу убить конкретную задачу, нужен ли мне список токенов? - person Norgul; 28.10.2016
comment
@Norgul: Да, вы бы. Или, по крайней мере, вам понадобится 5 разных токенов и какой-нибудь способ узнать, за каким из них наблюдает задача, которую вы хотите остановить. - person Jon Skeet; 28.10.2016