F# 中列表中的 Task.WaitAll

发布于 2024-10-19 11:19:39 字数 551 浏览 3 评论 0原文

我正在使用 F# 进行并行编程。对于固定数量的元素,例如 2 个元素 a1、a2 和一个函数 f,我可以执行以下操作:

let t1 = Task.Factory.StartNew(fun () -> f a1)
let t2 = Task.Factory.StartNew(fun () -> f a2)
Task.WaitAll(t1, t2)
t1.Result, t2.Result

我想知道如何对元素列表执行相同操作:

let ts = List.map (fun a -> Task.Factory.StartNew(fun () -> f a))
Task.WaitAll(ts)
List.map (fun (t: Task<_>) -> t.Result) ts

Visual Studio 发现 Task.WaitAll 无法接受任务< T>列表作为其参数。 Task.WaitAll 可以将 Task [] 作为其参数,但这没有意义,因为我需要获取 Result 进行下一次计算。

I am doing parallel programming using F#. With fixed number of elements, for example with 2 elements a1, a2 and a function f, I can do as follows:

let t1 = Task.Factory.StartNew(fun () -> f a1)
let t2 = Task.Factory.StartNew(fun () -> f a2)
Task.WaitAll(t1, t2)
t1.Result, t2.Result

I wonder how I could do the same with a list of elements:

let ts = List.map (fun a -> Task.Factory.StartNew(fun () -> f a))
Task.WaitAll(ts)
List.map (fun (t: Task<_>) -> t.Result) ts

Visual Studio spots that Task.WaitAll couldn't accept Task< T > list as its parameter. Task.WaitAll can have Task [] as its argument but it makes no sense because I need to get Result for next computation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夏の忆 2024-10-26 11:19:39

正如 Robert 所解释的,如果您想调用 WaitAll,则必须将元素序列转换为基本类型 Task,然后将其转换为数组。您可以为 Task 定义扩展成员,以使任务更简单:

type System.Threading.Tasks.Task with
  static member WaitAll(ts) =
    Task.WaitAll [| for t in ts -> t :> Task |]

我使用数组理解和转换而不是 Seq.cast 因为 Seq.cast 采用无类型的 IEnumerable - 因此 F# 为扩展方法推断出更好的类型。

另一种选择是根本不调用 WaitAll - 如果您不这样做,Result 属性将阻塞,直到任务完成。这意味着无论如何您都会阻塞线程(可能会有更多的阻塞,但我不确定它是否对性能影响太大)。如果您使用 List.map 收集所有结果,则行为几乎相同。

As Robert explains, if you want to call WaitAll, you'll have to cast the elements sequence to the base type Task and then convert it to an array. You can define your extension member for Task to make the tas simpler:

type System.Threading.Tasks.Task with
  static member WaitAll(ts) =
    Task.WaitAll [| for t in ts -> t :> Task |]

I'm using array comprehension and cast instead of Seq.cast because Seq.cast takes untyped IEnumerable - so F# infers better type for the extension method.

Another option is not to call WaitAll at all - if you don't do it, the Result properties will block until a task completes. This means that you'll block the thread anyway (there may be a bit larger number of blockings, but I'm not sure if it affects performance too much). If you use List.map to collect all results, the behavior would be almost the same.

安静被遗忘 2024-10-26 11:19:39

这是一个不幸的设计。 Task.WaitAll 使用 c# params 关键字允许您指定多个参数并将它们作为方法中的数组。它还使用 C# 的隐式转换,以便您可以为其指定 Task
在 F# 中,您必须通过显式转换并转换为数组来自行完成此操作:

let ts = [| 
     Task.Factory.StartNew(fun () -> 1)
     Task.Factory.StartNew(fun () -> 2)
     |]
Task.WaitAll(ts |> Seq.cast<Task> |> Array.ofSeq)

现在您可以从 ts 获取结果。

This is an unfortunate design. Task.WaitAll is using the c# params keyword to allow you to specify several arguments and have them be an array in the method. It also uses C#'s implicit casting so that you can give it Task<T>'s.
In F# you have to do this yourself by casting explicitly and converting to array:

let ts = [| 
     Task.Factory.StartNew(fun () -> 1)
     Task.Factory.StartNew(fun () -> 2)
     |]
Task.WaitAll(ts |> Seq.cast<Task> |> Array.ofSeq)

Now you can get the results from ts.

小清晰的声音 2024-10-26 11:19:39

数组也有映射,所以没有理由不能将任务放入数组中。

或者你可以转换为数组只是为了等待......

Array also has map, so there's no reason you can't put the tasks in an array.

Or you could convert to an array just for the waitall...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文