Порядок гарантии сообщений отправляется на процессор почтового ящика

У меня есть процессор почтового ящика, который получает постоянное число сообщений:

let consumeThreeMessages = MailboxProcessor.Start(fun inbox ->
        async {
            let! msg1 = inbox.Receive()
            printfn "msg1: %s" msg1

            let! msg2 = inbox.Receive()
            printfn "msg2: %s" msg2

            let! msg3 = inbox.Receive()
            printfn "msg3: %s" msg3
        }
    )

consumeThreeMessages.Post("First message")
consumeThreeMessages.Post("Second message")
consumeThreeMessages.Post("Third message")

Эти сообщения должны быть обработаны в точно отправленном заказе. Во время моего тестирования это распечатывает точно, что это должно:

First message
Second message
Third message

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

Second message // <-- oh noes!
First message
Third message 

Сообщения, как гарантируют, будут получены и обработаны в отправленном заказе? Или для сообщений действительно ли возможно быть полученным или обработанным не в порядке?

7
задан Ned Batchelder 13 July 2010 в 02:10
поделиться

1 ответ

Код в вашей функции consumerThreeMessages всегда будет выполняться по порядку из-за того, как работают асинхронные рабочие процессы F #.

Следующий код:

   async {
            let! msg1 = inbox.Receive()
            printfn "msg1: %s" msg1

            let! msg2 = inbox.Receive()
            printfn "msg2: %s" msg2

        }

Примерно переводится в:

async.Bind(
    inbox.Receive(),
    (fun msg1 -> 
         printfn "msg1: %s" msg1
         async.Bind(
             inbox.Receive(),
             (fun msg2 -> printfn "msg2: %s" msg2)
         )
    )
)

Когда вы смотрите на обессахаренную форму, становится ясно, что код выполняется последовательно. «Асинхронная» часть используется в реализации async.Bind , которая запускает вычисления асинхронно и «просыпается» по завершении, чтобы завершить выполнение. Таким образом, вы можете воспользоваться преимуществами асинхронных аппаратных операций и не тратить время на потоки ОС, ожидающие операций ввода-вывода.

Однако это не означает, что вы не можете столкнуться с проблемами параллелизма при использовании асинхронных рабочих процессов F #. Представьте, что вы сделали следующее:

let total = ref 0

let doTaskAsync() = 
    async { 
        for i = 0 to 1000 do 
            incr total
    } |> Async.Start()

// Start the task twice
doTaskAsync()
doTaskAsync()

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

Итак, чтобы ответить на ваш вопрос вкратце: в теле одного асинхронного блока все всегда будет выполняться по порядку. (То есть следующая строка после let! Or do! Не выполняется до тех пор, пока асинхронная операция не завершится.) Однако, если вы разделяете состояние между двумя асинхронными задачами, то все ставки отключены. В этом случае вам нужно будет рассмотреть возможность блокировки или использования параллельных структур данных, поставляемых с CLR 4.0.

8
ответ дан 7 December 2019 в 05:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: