2014-01-15 3 views
2

Я пытался научиться использовать асинхронные методы на основе сообщений. Ниже приведен упрощенный вариант того, что я пытался сделать. Я пытаюсь использовать конечный автомат в пределах MailboxProcessor внутри объекта. В целом кажется, что логика может быть гораздо более простой по сравнению с использованием методов, основанных на событиях. У меня проблема, но когда я пытаюсь использовать Async.Parallel. В коде, следующем за оператором printfn "Eval %i" v, производится оценка дважды для i1 & i2 вместо одного раза для каждого. Это заставляет меня думать, что я неправильно использую Async.Parallel. Существует ли альтернативный метод, который должен использоваться в асинхронном рабочем процессе?Как использовать Async.Parallel в асинхронном рабочем процессе?

type Input(v) = 

    let agent = 
     MailboxProcessor.Start(fun inbox -> 
      let rec loop() = 
       async { 
        let! (msg : AsyncReplyChannel<int>) = inbox.Receive() 
        printfn "Eval %i" v 
        msg.Reply(v) 
        return! loop() 
       } 
      loop()) 

    member this.Eval = agent.PostAndAsyncReply(fun r -> r) 

let i1 = Input(1) 
let i2 = Input(2) 

async { 
    let! nodeValues = [ i1; i2 ] 
         |> Seq.map(fun n -> n.Eval) 
         |> Async.Parallel 
    return nodeValues 
} 
|> Async.RunSynchronously 
+1

Вам не нужен блок 'async {}'. '[i1; i2] |> Seq.map (fun n -> n.Eval) |> Async.Parallel |> Async.RunSynchronously' будет работать. – Daniel

+1

Спасибо за комментарий, но фактическая реализация не выполняется синхронно. Мой вопрос заключается в том, как параллельно вычислять массив async-возвратов (в этом случае метод Eval) параллельно в async-потоке. См. [Link] (https://gist.github.com/mndrake/8440854#file-utopia-fsx-L121) для более крупного примера. – mndrake

+0

Возможно, просто передайте результат 'Async.Parallel' в' Async.Ignore>> Async.Start'? Что вы хотите делать с результатами «Eval» после их завершения? – Tarmil

ответ

3

Это ошибка в F # 3.0. Async.Parallel звонки Seq.toArray два раза. Запустите свой код под F # 3.1, и он будет печатать только один раз. Here's the fix in the F# source repository.

+0

Спасибо за помощь gradbot. Я не думал проверять источник github. Похоже, что метод «Async.Parallel» имеет достаточно зависимостей, что это больше, чем просто небольшой фрагмент, который мне понадобится, если я захочу использовать методологию 3.1 в VS2012. Когда я использую 'Array.map' и начиная с массива, входящего в метод Async.Parallel', вызов' Seq.toArray' эффективно игнорируется? Я предполагаю, что я спрашиваю, использует ли 'Array.map' в F # 3.0 решение проблемы или мне нужно избегать Async.Parallel в версии 3.0 вообще? Благодаря! – mndrake

+2

Конечно, да, если вы используете Array.map, он будет работать правильно. Ошибка будет возникать в источнике F #, но она просто преобразует ваш массив в последовательность, а затем обратно в массив дважды. Я считаю, что это ошибка дизайна. 'Async.parallel' должен принимать массив, а не последовательность. Это фактически не поддерживает ленивую оценку, и это может привести к тому, что некоторые пользователи сбиваются с пути. – gradbot

Смежные вопросы