#async-await #akka.net
Вопрос:
Я понимаю, что в целом асинхронность/ожидание в Акка.сетевые актеры блокируют почтовый ящик актера. В одном ReceiveAsync
обработчике я хотел бы иметь блокирующий раздел async/await, за которым следует конвейер, который не блокируется. Мне интересно, будет ли это достигнуто следующим образом:
class MyActor : ReceiveActor
{
public MyActor()
{
ReceiveAsync<MyMsg>(Handle);
}
private async Task Handle(MyMsg msg)
{
if (_someNotTrivialCondition)
{
// Run this on actor thread; blocking WANTED here.
// (Ie, I don't want DoSomeWorkAsync to begin until
// this await statement completes.)
await RunSomeNonTrivialCompensationLogic();
}
// Now an API call; blocking NOT WANTED here; just
// send the data back to the sender.
DoSomeWorkAsync().PipeTo(Context.Sender, Self,
success: result => new MyResponse(result)),
failure: ex => new MyResponse(ex));
}
}
У меня есть подозрение, что, возможно, в задачах, созданных в a ReceiveAsync
, есть что-то, что может привести к тому, что это не будет делать то, что я ожидаю.
Будет ли смешивание async/await и PipeTo
в том же ReceiveAsync
порядке работать, как описано в комментариях к коду?
Ответ №1:
Я не полный эксперт по Акка, но я использую его на регулярной основе.
Из вашего вопроса не совсем ясно, пытаетесь ли вы запустить await и pipeto одновременно или нет. Поэтому я стараюсь принимать во внимание оба сценария.
Прежде всего, я стараюсь избегать асинхронности/ожидания в своих актерах, если могу. Если я не могу, я пытаюсь изолировать шаги в разных актерах, с руководителем/контролирующим актером, содержащим логику и, возможно, изменяющим поведение (становящимся/неподходящим) актера в зависимости от некоторых значений (например, в вашем случае условие «_someNotTrivialCondition»). (Вы можете найти больше об этом в акка.чистая документация. Хорошим примером является конечный автомат.)
Отдельные шаги выполняются в «дочерних» актерах, и, конечно, мне нужно дождаться завершения задачи, но это должно быть сделано изолированным способом (в этом отдельном дочернем актере), и когда эта изолированная задача завершена, она «сообщает» руководящему актеру, что она выполнена, и выдает результат, например, в сообщении. Наблюдающий актер отреагирует на это сообщение, и, как уже говорилось ранее, легко изменить поведение актера и действовать по-другому.
Короче говоря, я стараюсь добиться большего количества реактивных (управляемых событиями/сообщениями) вычислений (с помощью tell), чем «ожидание» или «запрос».
Однако, чисто по теме вашего вопроса, если мы посмотрим на ваш пример, давайте объединим правила для async/await и оператора pipeTo в akka.net.
Ожидание заблокирует окно сообщений актеров, поэтому обработка этого актера не будет выполнена до тех пор, пока не будет достигнуто ожидание. После этого он открыт для других вещей, и сообщения могут быть обработаны снова. Кроме того, это все еще .net/c#, поэтому он не будет выполнять код после ожидания, если только ожидание больше не будет на месте. В конце концов, в этом и заключается вся цель заявления await ! Так что нет, он не будет выполнять код pipeto одновременно, если это ваше намерение. Если это ваше намерение, вы должны отбросить ключевое слово await, и оно должно выполняться одновременно и, на мой взгляд, должно работать нормально.
Пипетка отправит сообщение, если задача выполнена, как вы определили в своем примере. Опять же, если необходимо выполнить строку кода ожидания (условие верно), конвейер не будет выполняться до тех пор, пока ожидание больше не будет блокировать код (выполнение кода по умолчанию на c#/.net).
Таким образом, если ваш «Контекст.Отправитель» не является «Я», он должен отправить «успех» или «неудачу» этому «Отправителю-субъекту», и если этот субъект каким-то образом не заблокирован, он должен работать нормально, если он не намерен выполнять их одновременно.
Комментарии:
1. Ценю ваш вдумчивый ответ. Я не согласен с тем, чтобы избегать асинхронности/ожидания в актерах. Я подробно объяснил свою точку зрения Аарону Станнарду здесь ( petabridge.com/blog/top-7-akkadotnet-stumbling-blocks/… ) и получил в целом благоприятный ответ от него (и других). Я немного проясню свой вопрос, отредактировав его. Мой вывод из вашего ответа заключается в том, что, насколько вам известно, он должен работать так, как я ожидаю. Спасибо за ваши мысли!
2. Я думаю, что это скорее личный вкус, и да, я иногда использую async/await, но я стараюсь избегать создания «задач» самостоятельно (если я использую akka). Трудно найти людей, хорошо знающих akka, и использование обеих концепций не делает код более понятным. Я стараюсь избегать тупиков, и, честно говоря, широкое использование async/await является сложной задачей (чтобы избежать всевозможных волшебных действий). Лично, если бы я был так увлечен асинхронностью/ожиданием, я бы глубже изучил решение с микро-потоками, подобное тому, которое используется в игровом движке Stride с открытым исходным кодом, и оставил бы akka в стороне.
3. Читая ваш пример кода с дополнительной информацией, я не вижу причин, по которым он не должен работать должным образом. Я заметил, что вы не новичок ни в акка, ни в асинхронности/ожидании, так почему бы вам не проверить это и не выяснить для себя ? Не вижу причин, по которым это было бы трудно сделать тестовым случаем.
4. Еще раз спасибо. Пара ответов: «широкое использование async/await является проблемой» — я нахожу обратное: async/await, как правило, делает процесс прямым и легким для чтения, в то время как дочерние участники pipeTo менее прямолинейны (код становится более сложным по сравнению с бизнес-логикой). Но то, что я буду использовать, почти всегда определяется желаемым поведением: асинхронность/ожидание, чтобы использовать одноразовую гарантию субъекта в определенной последовательности шагов, для обеспечения пропускной способности. Re: «проверьте это и убедитесь сами» Я попробую это сделать; иногда смешивание блоков кода синхронизации и асинхронности в c# вызывает тонкие и неожиданные проблемы.
5. Я согласен, что если кто-то хочет выполнить «последовательные шаги», await может сделать это простым и читаемым (но акка тоже с изменением поведения). Но я не понимаю, зачем тогда вам понадобилась акка ? Блоки ожидания и акка предназначены для того, чтобы не блокировать («спрашивать» никто серьезно не советовал) и позволяют выполнять массовое параллельное выполнение кода/актеров. Массивный ! Если я буду ждать всего, это резко ограничит мою систему. Поэтому только для умеренных систем с более чем достаточной емкостью я бы посоветовал то, что вы предлагаете. Во всех остальных случаях я бы предложил реактивное программирование или акка, используемую реактивным способом (скажите, а не спрашивайте).