Нужно ли проверять токен отмены в Parallel.Foreach - PullRequest
1 голос
/ 19 сентября 2019

Вот документация для Как: отменить цикл Parallel.For или ForEach

И я написал метод для использования этой функции:

static async Task SpreadCheer(CancellationToken cancellationToken)
{
    cancellationToken.ThrowIfCancellationRequested();
    var friends = await GetFriends();

    Parallel.ForEach(friends,
            new ParallelOptions
                { CancellationToken = cancellationToken},
            friend=>
            {
                SendCake(friend);
                // (*1)
            });
}

В примере, приведенном в документации, в последней строке каждого цикла (*1).Это добавляет cancellationToken.ThrowIfCancellationRequested.

Это смущает меня, потому что тогда, почему я передал его в параллельные опции.Я также могу увидеть, что Parallel.Foreach уже сгенерирует после обработки предмета, если токен будет отменен.Он также бросает непосредственно перед началом любых циклов, если токен отменяется с самого начала.Мой опыт запуска и тестирования этого кода также свидетельствует о том, что не требуется явного выброса при отмене.

Нужно ли (даже я) ставить cancellationToken.ThrowIfCancellationRequested в положение (*1)?

Конечно, в документации есть пример этого, но он явно не упоминает об этом, и если там был модульный тест, охватывающий это, это не код, необходимый для прохождения этого теста.Насколько я могу сказать.


Дополнительный контекст:

Некоторый контекст при отмене.Причина отмены заключается в том, что у меня есть Azure WebJob для SpreadCheer, Spreading Cheer может занять некоторое время, и я хотел бы изящно прекратить распространение приветствия, если Azure отправит мне сигнал выключения.Я не хочу испортить чей-то торт.

1 Ответ

1 голос
/ 19 сентября 2019

Причина, по которой вы должны использовать token.ThrowIfCancellationRequested() в самих операциях, заключается в том, что параллельные операции будут выполняться до самого конца.

Parallel.ForEach проверяет токен только в начале и в конце операции, а не в промежутке между ними.

Так что если у вас много параллельных операций или операций, выполняющихся долго, вы будетехотите самостоятельно проверить отмену операции.

Поведение можно проверить с помощью следующего фрагмента, с или без token.ThrowIfCancellationRequested()

internal class Program
{
    private static void Main()
    {
        var source = new CancellationTokenSource();

        var token = source.Token;

        Task.Run(() =>
        {
            Parallel.ForEach(Enumerable.Range(1, 100), new ParallelOptions {CancellationToken = token},
                i =>
                {
                    for (var y = 0; y < 100; y++)
                    {
                        token.ThrowIfCancellationRequested();
                        Thread.Sleep(1000);
                        Console.WriteLine($"{i} {y}");
                    }
                });
        }, token);

        Console.WriteLine("press return to cancel...");
        Console.ReadLine();

        source.Cancel();

        Console.WriteLine("press return to exit...");
        Console.ReadLine();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...