簡體   English   中英

控制可能在F#中發生沖突的異步操作

[英]Controlling async actions that might conflict in F#

我要在F#中執行許多操作( Async<T> list )。 我可以並行執行大多數這些動作,但是由於文件鎖定等原因,有些動作可能會發生沖突。

對於每個動作,我可以生成一個“鍵”( int ),該鍵確定動作是否可能沖突:

  • 如果操作a具有鍵i ,操作b具有鍵j並且i = j ,則ab可能會沖突。 它們必須順序執行。
  • 如果動作a具有鍵i ,動作b具有鍵ji <> j ,則ab將永遠不會沖突。 它們可以並行執行。

我想以一種有效的方式執行我的動作(int * Async<T>) list ,並且沒有沖突。

我認為該過程將類似於:

  • 按按鍵將所有動作分組
  • 將每個組串行鏈接到一個Async
  • 並行運行每個鏈

如何在F#中實現呢?

這些問題通常如何處理?


我嘗試完全按順序執行:

let wrapTasks<'T> (tasks : (int * Async<'T>) list) : Async<'T list> = async {
  return
    tasks 
    |> Seq.map (fun (k, t) -> t |> Async.RunSynchronously)
    |> Seq.toList
}

這是一個可能的解決方案:

let wrapTasks (tasks : (int * Async<'T>) list) =
    tasks
    |> List.groupBy fst 
    |> Seq.map (fun (k, ts) -> async {
        for (i, t) in ts do
            let! r = t
            ()
    })
    |> Async.Parallel
    |> Async.RunSynchronously

通過一個輔助函數,對值x取一個“承諾”,對一組值acc取一個“承諾”:

module Async =
    let sequence x acc = async {
        let! x = x
        let! y = acc
        return x :: y
    }

我們可以按照tasks的“鎖ID”對tasks進行異步分組,對結果列表進行一些清理,然后將每個組sequence為單個async ,以“包含”該組結果的列表。 然后並行處理此列表。 ts : 'b list []可用后,我們將其展平:

let wrapTasks tasks = async {
    let! ts =
        tasks
        |> List.groupBy fst
        |> List.map (snd >> List.map snd)
        |> List.map (fun asyncs -> List.foldBack Async.sequence asyncs (async { return [] }))
        |> Async.Parallel
    return ts |> List.ofArray |> List.collect id
}

這可以用例如測試

List.init 50 (fun i -> i % 5, async {
    let now = System.DateTime.UtcNow.Ticks
    do! Async.Sleep 10
    return i, now })
|> wrapTasks
|> Async.RunSynchronously
|> List.groupBy snd
|> List.map (fun (t, rs) -> t, rs |> List.map fst)
|> List.sort

通過改變除數,我們可以調整並行度,並使自己確信該函數按預期運行:-)

  [(636766393199727614L, [0; 1; 2; 3; 4]); (636766393199962986L, [5; 6; 7; 8; 9]); (636766393200068008L, [10; 11; 12; 13; 14]); (636766393200278385L, [15; 16; 17; 18; 19]); (636766393200382690L, [20; 21; 22; 23; 24]); (636766393200597692L, [25; 26; 27; 28; 29]); (636766393200703235L, [30; 31; 32; 33; 34]); (636766393200918241L, [35; 36; 37; 38; 39]); (636766393201027938L, [40; 41; 42; 43; 44]); (636766393201133307L, [45; 46; 47; 48; 49])] 

全面披露:為了獲得這個不錯的結果,我不得不執行了幾次測試。 通常數字會有點小。

暫無
暫無

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

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