Обеспечение последовательности сообщений в Orleans при конвейерном выполнении

У меня есть три зерна (A, B и C), выполняющие разные задания в конвейере. GrainA передаст результат зерну B, а зерно B передаст результат зерну C. Я хочу гарантировать последовательную отправку сообщений между последовательными зернами, что может быть достигнуто ниже.

// client code
foreach(var i in list)
{
    await grainA.job(i);
}

//grain A code
async Task job(var i)
{
    DoSomeWorkA(i);
    await grainB.job(i);
}

//grain B code
async Task job(var i)
{
    DoSomeWorkB(i);
    await grainC.job(i);
}

//grain C code
async Task job(var i)
{
    DoSomeWorkC(i);
    Console.WriteLine(i);
}

Однако проблема с этим кодом заключается в отсутствии конвейерной обработки. зерну A дается чистый объект только тогда, когда текущий объект проходит через все зерна B и зерна C (из-за оператора ожидания). Один из способов получить конвейерную обработку — не использовать await, а напрямую отправлять объекты один за другим. Однако это приводит к доставке не по порядку, как описано в этот пост.

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


person Shengquan Ni    schedule 03.08.2018    source источник
comment
Таким образом, вы не хотите, чтобы разные задания выполнялись одновременно, но вы не хотите, чтобы они выполнялись последовательно. Так чего ты хочешь? Какие операции можно переупорядочивать друг относительно друга, а какие нельзя?   -  person Servy    schedule 04.08.2018
comment
Думайте об этом как о рабочем процессе, включающем различные операторы, которые должны выполняться один за другим. Оператор добавляет к объекту что-то, что может понадобиться последующим операторам. Следовательно, операторы должны выполняться последовательно, но конвейерно. т.е. когда объект-1 находится в зерне C, объект-2 должен быть в зерне B, объект-3 в зерне A.   -  person Shengquan Ni    schedule 04.08.2018
comment
Эти операторы сохраняют состояние? Почему порядок возврата имеет значение, если они просто возвращаются вверх по стеку к первоначальному вызывающему объекту? Если есть какая-то последовательность, которая важна, ее нет в приведенном выше коде.   -  person Reuben Bond    schedule 04.08.2018


Ответы (1)


Чтобы упростить программирование и анализ систем с высокой степенью параллелизма, Orleans ограничивает параллелизм и параллелизм каждой гранулы (единицы вычисления и состояния) до 1. Это означает, что одновременно будет обрабатываться не более одного сообщения и не более одного потока. будет выполнять данный код активации зерна в любой момент времени.

Это значительно упрощает то, на чем должен сосредоточиться разработчик, поскольку больше нет необходимости в примитивах синхронизации потоков, таких как блокировки.

Однако поведение настраивается. Если вам не нужно это «безопасное» поведение по умолчанию, вы можете пометить зерно как [Reentrant] или пометить отдельные методы как [AlwaysInterleave] в интерфейсе зерна.

Это позволяет одновременно обрабатывать множество сообщений. В каждой await точке метода зерна выполнение возвращается планировщику, и планировщик может начать обработку другого сообщения. Планировщик по-прежнему обеспечивает однопоточность для каждой активации зерна, поэтому блокировки по-прежнему не нужны, но сообщения могут чередоваться в этих await точках (т. е. совместная многозадачность). Это означает, что теперь разработчику необходимо рассмотреть, как внутреннее состояние может быть изменено другими запросами между этими await точками.

Дополнительную информацию см. на странице повторного входа в документации Orleans.

Чтобы увеличить параллелизм (для задач, связанных с процессором), разработчик может использовать Работники без гражданства или Внешний Задачи через Task.Run или аналогичный.

Также обратите внимание, что сообщения, отправленные от активации зерна A к активации зерна B, всегда будут отправляться по порядку, независимо от конфигурации параллелизма. То же самое и с результатами, отправленными из B в A. Обратите внимание, что по-прежнему становится все труднее рассуждать о порядке сообщений после увеличения параллелизма, поскольку второй вызов B может завершиться до первого вызова и, следовательно, его результат будет отправлен раньше, также. Это означает, что результаты могут оказаться полученными не по порядку. Я полагаю, что это то, чего вы ожидаете, хотя.

person Reuben Bond    schedule 03.08.2018
comment
Ой. это может вызвать проблему. Пожалуйста, скажите мне, правильно ли мое понимание ниже. Assume I use [Reentrant]. If grain-A makes request-1 to grain-B and awaits that, then it is free to make request-2 too to grain-B. Now in grain-B, the request-2 might arrive earlier than request-1. Thus, the message order is no more intact. - person Shengquan Ni; 04.08.2018
comment
Нет, я сказал, что запросы всегда в порядке. Это ответы, которые могут быть не в порядке, потому что запросы могут занимать разное время для обработки (и я предполагаю, что вы делаете некоторое ожидание, чтобы выполнить выполнение, и в запросе может быть некоторое неупорядоченное чередование). Какого поведения вы пытаетесь добиться? Есть ли у этих зерен какое-то состояние? Другими словами, можете ли вы масштабироваться, используя рабочие процессы без сохранения состояния или несколько гранул на каждом этапе? - person Reuben Bond; 04.08.2018
comment
Дайте мне знать, если это ответит на ваш вопрос. Мне интересно лучше понять ваш сценарий. - person Reuben Bond; 04.08.2018
comment
Мы пытаемся разработать механизм обработки рабочего процесса с использованием зерна. Рабочий процесс может содержать множество операторов, таких как операторы SQL или модели машинного обучения. Каждый оператор может быть представлен гранулой (однако, ради параллелизма, у нас может быть несколько гранул на оператор). Ряд строк проходит через этот рабочий процесс, и операторы воздействуют на них. В настоящее время мы не видим, чтобы операторы имели состояние (но очень рано говорить, что нам не понадобится состояние). - person Shengquan Ni; 04.08.2018
comment
Мы ничего не возвращаем предыдущим операторам (поскольку строки перемещаются вперед в рабочем процессе и, наконец, помещаются в поток). Итак, что нас волнует, так это то, что выполнение запросов у вызываемого объекта происходит в том же порядке, что и вызываемый вызывающий объект. Однако вы уверены, что запросы в порядке? Потому что в предыдущем вопросе мы увидел, что выполнение на зерне не было в порядке вызова на клиенте (т.е. последовательность сообщений не сохраняется Орлеаном). Или ожидание меняет это поведение? - person Shengquan Ni; 04.08.2018
comment
Порядок сохраняется, однако, если вы отправляете сообщения 1, 2 и 3 зерну, возможно, что силос, который он активировал при падении после сообщения 2, и зерно повторно активируется для сообщения 3. Не дожидаясь каждого сообщения, вы можете гарантировать, что 1 и 2 обрабатываются до 3. Сообщения в несуществующий бункер не будут автоматически повторно отправляться. Поэтому я бы предложил либо не включать чередование (и, следовательно, обрабатывать одно сообщение за раз), либо использовать потоки для достижения желаемого поведения, либо использовать специальное решение (возможно, с использованием порядковых номеров), чтобы гарантировать, что каждое зерно в графе обрабатывает сообщения в - person Reuben Bond; 04.08.2018
comment
Опечатка: не ожидая каждого сообщения, вы не можете гарантировать, что 1 и 2 будут обработаны раньше, чем 3 - person Reuben Bond; 05.08.2018