1

Async<T> list在 F# 中有很多动作 ( ) 要执行。我可以并行执行大多数这些操作,但有些可能由于文件锁定等而发生冲突。

对于每个动作,我可以生成一个“键”(int)来确定动作是否可能发生冲突:

  • 如果 actiona有 keyi并且 actionb有 key jand i = j,那么aandb可能会发生冲突。它们必须按顺序执行。
  • 如果 actiona有 keyi并且 actionb有 key jand i <> j,那么aandb永远不会冲突。它们可以并行执行。

我想以(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
}
4

2 回答 2

1

这是一个可能的解决方案:

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
于 2018-10-31T22:33:37.743 回答
1

使用辅助函数将“承诺”作为一个值x,将一个“承诺”作为一组值acc

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

我们可以tasks按它们的“锁定 ID”异步分组,稍微清理一下结果列表,然后将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])]

完全披露:为了得到这个好结果,我不得不执行几次测试。通常数字会有点偏。

于 2018-11-01T03:22:50.620 回答