如何启动多个`Async<'T>`并依次等待它们?

huangapple go评论66阅读模式
英文:

How to start many `Async<'T>` and wait for them one at a time?

问题

我有一个小的命令行应用程序,它启动了一堆昂贵的计算,并在它们完成时报告一些输出。我的目标是并行运行昂贵的计算,但要在计算进行时报告进度。

目前我是通过 Task 来处理的。

// 任务会在创建后立即开始
let tasks = lst |> List.map (fun x -> Task.Run(fun () -> expensiveComputation x))
for task in tasks do
  match! task |> Async.AwaitTask with
  // ...

expensiveComputation 是纯的,但我的 for 循环体不是。我会在信息到达用户时打印出来。注意:这意味着稍后的任务可能在用户不知情的情况下“完成”,并且当它们被打印出来时,看起来好像一堆任务在“同时”完成。我只关心大致上显示进度。

是否有一种方法可以在不使用 Task<'T> 的情况下做到这一点?Async.Parallel 在这里不起作用,因为它将所有任务组合成一个大的 Async<'T[]>。用户在所有任务完成之前看不到进度。

我觉得这可以通过一些递归的组合来实现,但我还没有找到方法。

我知道我有一个“可行”的解决方案 - 我有兴趣看看是否可能作为一种“F#本地”解决方案。我正在学习F#,如果可能的话,不想涉及C#风格的代码。

英文:

I have a little command line app that starts a bunch of expensive computations, and reports some output as they finish. My goal is to run the expensive computations in parallel, but report progress as it's happening.

Right now I'm handling it with Task.

// Tasks eagerly start as soon as they&#39;re created
let tasks = lst |&gt; List.map (fun x -&gt; Task.Run(fun () -&gt; expensiveComputation x))
for task in tasks do
  match! task |&gt; Async.AwaitTask with
  // ...

expensiveComputation is pure, but the body of my for loop is not. I print out information as it comes to the user. Note: this does mean that later tasks can "finish" without the user knowing, and it looks like a bunch finish "at the same time" when they get printed. I only really care roughly that progress is shown.

Is there a way to do this without dipping into Task&lt;&#39;T&gt;? Async.Parallel doesn't work here because it combines everything into one big Async&lt;&#39;T[]&gt;. The user doesn't see progress until everything's done.

I feel like this can be accomplished with some combination of recursion but I haven't found it yet.

I understand I have a "working" solution - I'm interested in seeing if this type of behavior is possible as an "F#-native" solution. I'm learning F# and don't want to dip into C#-style code if possible.

答案1

得分: 1

要急切地启动一个async,请查看Async.StartChild

let expensiveComputation (x : int) =
  async {
    do! Async.Sleep (x * 1_000)

    return x
  }

async {
  let! jobsRunningInBackground =
    Async.Parallel
      [
        for i = 0 to 4 do
          Async.StartChild (expensiveComputation i)
      ]

  for job in jobsRunningInBackground do
    let! x = job

    printfn "完成的任务 %i" x

  printfn "所有任务完成"
}
|> Async.RunSynchronously
英文:

To eagerly start an async, look at Async.StartChild.

let expensiveComputation (x : int) =
  async {
    do! Async.Sleep (x * 1_000)

    return x
  }

async {
  let! jobsRunningInBackground =
    Async.Parallel
      [
        for i = 0 to 4 do
          Async.StartChild (expensiveComputation i)
      ]

  for job in jobsRunningInBackground do
    let! x = job

    printfn $&quot;Completed job %i{x}&quot;

  printfn &quot;All jobs completed&quot;
}
|&gt; Async.RunSynchronously

huangapple
  • 本文由 发表于 2023年7月11日 01:00:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76655868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定