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

Asynchronous F#如何运行多个异步任务并等待第一次完成的结果?,asynchronous,f#,Asynchronous,F#,有许多例子说明了如何在类似f#的环境中执行异步任务 但是我如何异步地只等待第一个结果呢 例如,如果我想运行一些并行查找任务,并且我想在获得第一个成功结果时进行更深入的搜索。(基本上相当于任务。当在C#world中出现任何时……我能想到的最简单的实现如下所示: open FSharp.Control let getOneOrOther () = let queue = BlockingQueueAgent(1) let async1 = async { d

有许多例子说明了如何在类似f#的环境中执行异步任务

但是我如何异步地只等待第一个结果呢


例如,如果我想运行一些并行查找任务,并且我想在获得第一个成功结果时进行更深入的搜索。(基本上相当于
任务。当在C#world中出现任何
时……

我能想到的最简单的实现如下所示:

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

在以下代码段中可以找到通用解决方案:


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

似乎这个解决方案足够简单,无阻塞,适合我的情况

let any (list: Async<'T>[])=
    let tcs = new TaskCompletionSource<'T>()

    list |> Array.map (fun wf->Async.Start (async{
                let! res=wf
                tcs.TrySetResult (res) |> ignore
            }))
         |> ignore

    Async.AwaitTask tcs.Task

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 }

printfn "%d" <| ([|async1;async2|] |> any |> Async.RunSynchronously)
let any(列表:Async()
list |>Array.map(fun wf->Async.Start(Async{
让!res=wf
tcs.TrySetResult(res)|>忽略
}))
|>忽略
Async.AwaitTask tcs.Task
设async1=async{
do!Async.Sleep(System.Random().Next(10002000))
返回1}
设async2=async{
do!Async.Sleep(System.Random().Next(10002000))
返回2}
printfn“%d”any |>Async.RunSynchronously)

基于事件的另一个实现:

let Choice (asyncs: seq<Async<'T>>) : Async<'T> =
    async {
        let e = Event<'T>()
        let cts = new System.Threading.CancellationTokenSource()
        do Async.Start(
            asyncs
            |> Seq.map (fun a -> async { let! x = a in e.Trigger x })
            |> Async.Parallel
            |> Async.Ignore,
            cts.Token)
        let! result = Async.AwaitEvent e.Publish
        cts.Cancel()
        return result
    }
let选择(异步:seq=
异步的{

设e=Event我会使用类似于:

let any asyncs =
    async {
        let t = 
            asyncs
            |> Seq.map Async.StartAsTask
            |> System.Threading.Tasks.Task.WhenAny
        return t.Result.Result }

你是否希望其他任务被中止,或者只是让它们在后台进行?我不想中止它们,但可能在将来,我想将此方法扩展到WaitForAnysuccesfullandFaiFallFail。我在f#是新手。在scala world中,用未来和承诺来实现这一点非常简单。值得注意的是从F#4.5开始,这是作为
Async.Choice
内置的。我很惊讶在Async中没有这样的东西。谢谢链接。这不是阻塞吗?如何将
WhenAny
的结果管道化到
Async.waittask
,使用
let!t
,然后返回
t.result
?@nphx:你能添加吗请将您的建议作为新答案或使用要点?
let Choice (asyncs: seq<Async<'T>>) : Async<'T> =
    async {
        let e = Event<'T>()
        let cts = new System.Threading.CancellationTokenSource()
        do Async.Start(
            asyncs
            |> Seq.map (fun a -> async { let! x = a in e.Trigger x })
            |> Async.Parallel
            |> Async.Ignore,
            cts.Token)
        let! result = Async.AwaitEvent e.Publish
        cts.Cancel()
        return result
    }
let any asyncs =
    async {
        let t = 
            asyncs
            |> Seq.map Async.StartAsTask
            |> System.Threading.Tasks.Task.WhenAny
        return t.Result.Result }