Остановка против разрыва в параллели.

Мне трудно понять loopState.Stop() и loopState.Break(). Я прочитал MSDN и несколько сообщений об этом, но я все еще в замешательстве.

Насколько я понимаю, каждый разделитель итераций предоставляет оставшиеся индексы для обработки потоков, и loopState.Stop() останавливает все потоки, а loopState.Break() останавливает текущий поток.

Однако давайте рассмотрим следующую ситуацию:

Parallel.For(0, 100, (i, loopState) =>
{
    if (i >= 10) 
        loopState.Break();
    Debug.Write(i);
});

Для этого цикла у меня есть следующий результат:

0 25 1 2 3 4 5 6 7 8 9 10 

Я понятия не имею, почему в результате есть 10 и 25 чисел.

Кто-нибудь может помочь?

Постскриптум У меня i5 520M CPU (2 ядра => 4 темы)

Ответ 1

loopState.Break() не нарушает функцию, как a return. Таким образом, строка после loopState.Break() будет выполнена. После того, как эта область завершилась для этого числа, for проверяет, был ли вызов loopState.Break(). Если это так, все циклы разрешены для продолжения до тех пор, пока не будет достигнуто число, которое называется Break.

В вашем примере петли с 0 до 24 будут прерываться одновременно с циклом 25 до 49 (и отображать их "сломанные" номера).

Петля 50..74 и 75..99 даже не начнется, потому что вторая петля 25..49 уже прервала всю операцию, так как их наблюдающие числа больше, чем разрывное число 10.

Ответ 2

Из документация Break():

Разрыв может использоваться для связи с циклом, чтобы никакие другие итерации после текущей итерации не выполнялись. Например, если Break вызывается из 100-й итерации цикла for, повторяющегося параллельно от 0 до 1000, все итерации менее 100 должны выполняться, но итерации от 101 до 1000 не нужны.

Это означает, что текущая итерация будет завершена (так что 10 будет напечатано). Break() также не может перемещаться во времени, поэтому 25 останется напечатанным. Что означает Break(), означает, что никакие новые итерации за пределами 10 не будут запущены.

Ответ 3

if (i >= 10) loopState.Break(); все равно продолжит текущую итерацию. Так печатается 10.

Однако итерации (i >= 10) после вызванного loopState.Break() не запускаются.

Но почему печатается 25? На следующем рисунке объясняется, почему. Поскольку у вас есть 4 потока, 0-99 будет разделено как 4.

1-я строка имеет: 0-24.
Вторая нить имеет: 25 - 49.
Третья нить имеет: 50 - 74.
Четвертая нить имеет: 75 - 99.

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

Дополнительные итерации могут выполняться, если они уже были запущены при вызове Break.

Поскольку второй поток запускается почти в то же время, что и 1-й поток, поэтому 0, 25 печатается. Затем if (i >= 10) loopState.Break(); вызывается при циклировании 25 во втором потоке.

Петли в 3-м и 4-м потоках не запускались до появления Break(), поэтому любое число, большее, чем 10, не печаталось.

image ref: http://www.albahari.com/threading/part5.aspx

Ответ 4

Разрыв гарантирует, что все итерации, которые в данный момент запущены, будут завершены.

Остановка просто завершает все.

Ответ 5

Все методы из статического класса Parallel возвращают ParallelLoopResult. Этот объект имеет два свойства - IsCompleted и LowestBreakIteration

Когда мы используем loopState.Break(), LowestBreakIteration возвращает целое число, которое представляет самую низкую итерацию, из которой вызывается оператор Break

Когда мы используем loopState.Stop(), LowestBreakIteration Возвращает null

Ответ 6

Самый простой ответ:

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

difference - stop - прерывает итерацию, в которую она вызвала, и break не делает.

Ответ 7

void Log(string prefix, bool isBreak=false) 
{
    var msg = isBreak ? " Break" : "";
    Console.WriteLine($"{prefix} task: {Task.CurrentId.ToString().PadLeft(3,'0')} {msg}");

}
long lockFlag=0;
Parallel.For(0, 130, (i, loopState) =>
{
    if (i >= 10 && Interlocked.Read(ref lockFlag)==0)
    {
        lockFlag=Interlocked.Increment(ref lockFlag);
        loopState.Break();
        //Statement after break will still execute for current iteration
        Log(i.ToString().PadLeft(3,'0'),true);
    }
    else
    {
      Log(i.ToString().PadLeft(3,'0')); 
    }
});   

enter image description here

Задачи "8904" продолжают работать, чтобы завершить все итерации менее 25. Очевидно, что если итерации уже завершены, которые представляют значения, превышающие 25, тогда не будет возможности отката.

Если вы хотите как можно скорее завершить цикл и не заботитесь о том, чтобы все предыдущие итерации были завершены, ParallelLoopState имеет другой метод Stop(). Метод Stop пытается завершить цикл как можно скорее - после выдачи, никакая задача цикла не начнет новую итерацию