Гарантийный порядок сообщений, отправленных на процессор почтовых ящиков - PullRequest
7 голосов
/ 08 февраля 2010

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

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

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

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

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

1 Ответ

8 голосов
/ 08 февраля 2010

Код в вашей функции consumeThreeMessages всегда будет выполняться по порядку, так как асинхронные рабочие процессы 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)
         )
    )
)

Когда вы смотрите на desugared форму, становится ясно, что код выполняется последовательно. Часть 'async' вступает в игру в реализации 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! Или do! Не выполняется до тех пор, пока не завершится асинхронная операция.) Однако, если вы разделяете состояние между двумя асинхронными задачами, все ставки отключаются. В этом случае вам нужно будет рассмотреть возможность блокировки или использования параллельных структур данных, которые поставляются с CLR 4.0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...