F#如何运行几个异步任务并等待第一次完成的结果?

Ale*_*ois 10 f# asynchronous

有许多例子如何在f#中执行异步任务

[dowork 1; work 2]
|> Async.Parallel
|> Async.RunSynchronously
Run Code Online (Sandbox Code Playgroud)

但是我怎么能异步地等待第一个结果呢?

例如,如果我想运行一些并行查找任务,并且我想在获得第一个成功结果时更深入地搜索.

kvb*_*kvb 6

我会用以下的东西:

let any asyncs =
    async {
        let t = 
            asyncs
            |> Seq.map Async.StartAsTask
            |> System.Threading.Tasks.Task.WhenAny
        return t.Result.Result }
Run Code Online (Sandbox Code Playgroud)


eir*_*rik 5

可以在以下代码段中找到通用解决方案:http://fssnip.net/dN

Async.Choice可以嵌入到任何异步工作流中,就像Async.Parallel.可选的输出类型编码子计算可以在没有令人满意的结果的情况下完成的可能性.


mav*_*vnn 3

我能想到的最简单的实现看起来像这样:

open FSharp.Control

let getOneOrOther () =
    let queue = BlockingQueueAgent(1)
    let async1 = async {
            do! Async.Sleep (System.Random().Next(1000, 2000))
            do! queue.AsyncAdd(1) } |> Async.Start
    let async2 = async {
            do! Async.Sleep (System.Random().Next(1000, 2000))
            do! queue.AsyncAdd(2) } |> Async.Start

    queue.Get()

for i in 1..10 do
    printfn "%d" <| getOneOrOther ()

Console.ReadLine () |> ignore
Run Code Online (Sandbox Code Playgroud)

它依赖于FSharpx项目的阻塞队列实现,您可能会出于其他原因需要它。但是,如果您不想要任何依赖项System.Collections.Concurrent,还包括一个阻塞队列,其界面稍微不太好。

对于内置取消功能的更通用版本,下面的版本采用 aSeq<unit -> Async<'T>>并返回第一个结果,取消所有其他结果。

open FSharp.Control
open System.Threading

let async1 () = async {
        do! Async.Sleep (System.Random().Next(1000, 2000))
        return 1 }
let async2 () = async {
        do! Async.Sleep (System.Random().Next(1000, 2000))
        return 2 }

let getFirst asyncs =
    let queue = BlockingQueueAgent(1)
    let doWork operation = async { 
            let! result = operation()
            do! queue.AsyncAdd(result) }
    let start work =
        let cts = new CancellationTokenSource()
        Async.Start(work, cts.Token)
        cts
    let cancellationTokens =
        asyncs
        |> Seq.map doWork
        |> Seq.map start
    let result = queue.Get()
    cancellationTokens
    |> Seq.iter (fun cts -> cts.Cancel(); cts.Dispose())
    result

for i in 1..10 do
    printfn "%A" <| getFirst [async1;async2]

Console.ReadLine () |> ignore
Run Code Online (Sandbox Code Playgroud)