Позволять! выполняется в последовательности?

У меня сложилось впечатление, что давай! в F# был достаточно умен, чтобы извинять последовательности назначений в параллели. Однако в следующем примере показано другое поведение: назначение a, b, c выполняется синхронно.

    let sleep sec =
        async
            {
                System.Threading.Thread.Sleep(sec * 1000)
                return sec
            }

    let bar = async 
                {
                    let! a = sleep 1
                    let! b = sleep 3
                    let! c = sleep 3
                    return a+b+c
                }

    let foo = Async.RunSynchronously(bar)
    printfn "%d" foo

Это так и должно быть?

И если я хочу выполнить a, b, c параллельно, я должен использовать Async.Parallell ... |> Async.RunSynchronously ... тогда?

Приведенный выше пример, конечно, бесполезен, реальный сценарий использования - это что-то вроде запроса к БД и одновременного вызова некоторых веб-сервисов.

2 ответа

Решение

Как указывает Ричард, асинхронные рабочие процессы все еще полностью последовательны. Я не думаю, что какие-либо проекты, пытающиеся сделать полностью автоматическое распараллеливание, были полностью успешными, потому что делать это слишком сложно.

Однако асинхронные рабочие процессы все еще упрощают распараллеливание. Ключевым моментом является то, что они позволяют выполнять ожидание без блокировки потоков (что важно для масштабируемости), а также поддерживают автоматическую отмену и простую обработку исключений, чтобы упростить вашу жизнь. Существуют различные шаблоны, позволяющие распараллеливать код в асинхронных рабочих процессах.

  • На основе задач вы можете запустить свои три задачи в фоновом режиме, а затем подождать, пока все они не будут выполнены (это, вероятно, то, что вы ожидали, поэтому вот как написать это явно):

    let bar = async  { 
      let! atask = sleep 1 |> Async.StartChild
      let! btask = sleep 3 |> Async.StartChild
      let! ctask = sleep 3 |> Async.StartChild
      let! a = atask
      let! b = btask
      let! c = ctask
      return a + b + c } 
    
  • Параллельная передача данных - если у вас есть несколько рабочих процессов одного типа, вы можете создать рабочий процесс, который запускает их все параллельно, используя Async.Parallel, Когда вы тогда используете let! он запускает все три задачи и ожидает их завершения:

    let bar = async  { 
      let! all = Async.Parallel [ sleep 1; sleep 3; sleep 3 ]
      return all.[0] + all.[1] + all.[2] } 
    

У Дона Сайма есть статья, в которой обсуждаются различные шаблоны, основанные на асинхронных рабочих процессах, и вы можете найти исчерпывающий пример в примере финансовой панели.

let!в async block (или, точнее, "выражение вычисления") выполняет выражение асинхронно, но блок в целом все еще выполняется линейно. Это преимущество async Выражения вычислений: сделать последовательность зависимых асинхронных операций намного проще для написания, выполнив передачу продолжения за вас.

(Другие типы вычислительных выражений предоставляют свою семантику для let!, yield!, так далее.)

Для выполнения параллельного / параллельного выполнения вам нужно несколько async выражения выполняются отдельно.

Я был под впечатлением

Вы неправильно поняли (вполне понятно).

Другие вопросы по тегам