Использовать Post или PostAndAsyncReply с F# для MailboxProcessor?
Я видел разные фрагменты, демонстрирующие Put
сообщение, которое возвращается unit
с F# MailboxProcessor
, В некоторых только Post
метод используется в то время как другие используют PostAndAsyncReply
с ответным каналом, немедленно отвечающим после обработки сообщения. При проведении некоторого тестирования я обнаружил значительную задержку во время ожидания ответа, поэтому кажется, что если вам не нужен реальный ответ, вы должны использовать Post
,
Примечание: я начал задавать это в другой ветке, но посчитал полезным опубликовать полный вопрос. В другом потоке Томас Петричек упомянул, что в канале ответа можно использовать механизм ожидания, чтобы гарантировать, что вызывающий абонент задерживается до Put
сообщение было обработано.
Использует ли PostAndAsyncReply
помочь с упорядочением сообщений, или это просто заставить паузу, пока не обработано первое сообщение? С точки зрения производительности Post
появляется правильное решение. Это точно?
Обновить:
Я просто подумал о причине, почему PostAndAsyncReply
может быть необходимо в BlockingQueueAgent
пример: Scan
используется, чтобы найти Get
сообщения, когда очередь заполнена, поэтому вы не хотите Put
а потом Get
до предыдущего Put
завершено.
2 ответа
Я думаю, что я в целом согласен с вашим резюме - имеет смысл, что PostAndAsyncReply
медленнее, чем Post
поэтому, если вызывающей стороне не нужно получать уведомление от агента, когда операция (например, помещает значение в очередь) завершается, он определенно должен предоставить способ сделать это, используя только Post
, Дело в том, что PostAndAsyncReply
намного медленнее, вероятно, означает, что некоторые агенты должны предоставить обе опции и позволить вызывающей стороне решить.
Что касается конкретного примера BlockingQueueAgent
(или аналогичный, который я использовал для реализации одноразового буфера), типичное применение агента - решение проблемы потребитель-производитель. В проблеме потребитель-производитель мы хотим заблокировать производителя, когда очередь заполнена, и заблокировать потребителя, когда она пуста. Сеть BlockingCollection
поддерживает только синхронную блокировку, что немного плохо (то есть может блокировать весь пул потоков).
Использование BlockingQueueAgent
который отправляет Put
использование сообщений PostAndAsyncReply
, мы можем подождать, пока элемент не будет добавлен в очередь асинхронно (поэтому он блокирует производителя, но без блокировки потоков!) Примером типичного использования является конвейер обработки изображений, который я написал некоторое время назад. Вот один фрагмент из этого:
// Phase 2: Scale to a thumbnail size and add frame
let scalePipelinedImages = async {
while true do
let! info = loadedImages.AsyncGet()
scaleImage info
do! scaledImages.AsyncAdd(info) }
Этот цикл неоднократно получает изображение от loadedImages
очередь, выполняет некоторую обработку и записывает результат в scaledImages
, Блокировка с использованием очереди (как при чтении, так и при записи) управляет параллелизмом, так что шаги конвейера выполняются параллельно, но не продолжают загружать все больше и больше изображений, если конвейер не может обрабатывать их с требуемой скоростью.
Мой совет - спроектировать вашу систему, чтобы вы могли использовать Post
как можно больше.
Эта технология была разработана для асинхронного параллелизма, цель которого - запускать и забывать сообщения. Идея ожидания ответа идет прямо вразрез с этим.