Код в вашей функции 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.