簡體   English   中英

F# 使用 Async.Parallel 並行運行 2 個任務

[英]F# using Async.Parallel to run 2 tasks in parallel

假設我有這兩個功能:

let dowork n =
    async {
        do printfn "work %d" n
    }

let work i = async {
  do! Async.Sleep(2000)
  printfn "work finished %d" i }

我將如何使用 Async.Parallel 同時運行它們並等待兩者完成后再繼續?

如前所述,您只需將異步函數放入序列中並將它們傳遞給Async.Parallel

但是,如果您需要執行返回不同類型結果的不同作業,則可以使用Async.StartChild

let fn1 = async {
        do! Async.Sleep 1000
        printfn "fn1 finished!"
        return 5
    }

let fn2 = async {
        do! Async.Sleep 1500
        printfn "fn2 finished!"
        return "a string"
    }

let fncombined = async {
        // start both computations simultaneously
        let! fn1 = Async.StartChild fn1
        let! fn2 = Async.StartChild fn2

        // retrieve results
        let! result1 = fn1
        let! result2 = fn2

        return sprintf "%d, %s" (result1 + 5) (result2.ToUpper())
    }

fncombined
|> Async.RunSynchronously
|> printfn "%A"

Async.Parallel采用一系列異步。 在這種情況下,我傳遞一個列表。

[dowork 1; work 2]
|> Async.Parallel
|> Async.RunSynchronously
|> ignore

如果要返回不同類型的數據,請使用Discriminated Union

type WorkResults =
    | DoWork of int
    | Work of float32

let dowork n =
    async {
        do printfn "work %d" n
        return DoWork(n)
    }

let work i = async {
  do! Async.Sleep(2000)
  printfn "work finished %d" i 
  return Work(float32 i / 4.0f)
}

[dowork 1; work 2]
|> Async.Parallel
|> Async.RunSynchronously
|> printf "%A"

產量

work 1
work finished 2
[|DoWork 1; Work 0.5f|]

當任務數量在編譯時固定時,我喜歡這樣的 helper function:

module Async = 

  let parallel2 a b = 
    async {
      // Start both tasks
      let! x = Async.StartChild a
      let! y = Async.StartChild b

      // Wait for both to finish
      let! i = x
      let! j = y

      // Return both results as a strongly-typed tuple
      return i, j
    }

用法如下:

let work1 = async { return 1 }

let work2 = async { return "a" }

let work1And2 = 
  async {
    let! (a, b) = Async.parallel2 work1 work2

    printfn "%i %s" a b
  }

請注意任務的不同類型。 這可能非常有用!

我們可以為Async<Unit>添加一個額外的助手,因為()((), ())具有相同的語義:

module Async = 

  // ...

  let doParallel2 (a : Async<Unit>) (b : Async<Unit>) =
    parallel2 a b
    |> Async.Ignore

然后應用於您的場景:

async {
  do! Async.doParallel2 (dowork 1) (work 2)
}

F# 中的新功能是應用程序 有了這些,我們可以擴展async計算表達式以支持並行and! 關鍵詞!

這是擴展名:

type AsyncBuilder with
  member this.Bind2(a, b, f) =
    async {
      let! x = Async.StartChild a
      let! y = Async.StartChild b

      let! i = x
      let! j = y

      return f (i, j)
    }

用法如下:

let work1And2 =
  async {
    let! a = work1
    and! b = work2

    printfn "%i %s" a b
  }
    let makeBoxed (job: Async<'a>) : Async<obj> = 
        async { 
            let! result = job
            return box result
        }
    
    let mixedParallel2 (a: Async<'T1>) (b: Async<'T2>): Async<'T1*'T2> =
        async {
            let! results = Async.Parallel [| makeBoxed a ; makeBoxed b |]
            return (unbox<'T1> results.[0]), (unbox<'T2> results.[1])
        }

與其他答案不同,這個答案正確處理異常和取消。 例如,當其中一個異步計算引發異常時,生成的組合計算會立即退出並引發該異常,而不是等待其他計算完成。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM