我在 F# 控制台应用程序的主函数中有以下代码。
let main ... =
let list = // get a list
list
|> Seq.iter(fun i -> ....)
()
然后我尝试并行运行这些项目。
let list = // get a list
list
|> Seq.map(fun i -> async { .... })
|> Seq.toArray
|> Async.Parallel
|> Async.RunSynchronously // ERROR
// |> ignore
()
它得到了错误
错误 FS0001 此表达式应具有类型 "单位" 但这里有类型 "单位 []"
添加|> ignore
将导致 F# 无法运行Seq.map()
中的代码。
我故意不回答你的问题本身。相反,我将分享一种方法,如果您偶尔偶然发现异步工作流,您可以使用该方法进行救援。诀窍是完全采用 F# 推广的探索性编程风格。
让我们从一个最小的异步工作流开始,这可能是async { () }
.在 FSI 中进行评估
let dummywf = async { () }
收益 率
val dummywf : Async<unit>
显示dummywf
值确实是一个异步工作流(尽管是一个非常虚拟的工作流(。
异步工作流可以组合成序列,最简单的一种可能是列表[ dummywf ]
。评估它揭示了类型Async<unit> list
,它代表了所寻求的类型。
现在,让我们回顾一下Async.Parallel
方法的签名,它是seq<Async<'T>> -> Async<'T[]>
的,即它采用一系列异步计算并将其转换为异步计算,最终通过异步评估其成员进行评估将产生'T
值数组。签入 FSI,表达式
[ dummywf ] |> Async.Parallel
计算为类型Async<unit []>
的值,确实。
最后,让我们通过最简单的Async.RunSynchronously<'T>
方法组合它来实现这个值,该方法采用 Async<'T>(在我们的例子中是Async<unit []>
类型的值(并返回类型'T
的评估结果(在我们的例子中是unit []
.计算组合表达式
[ dummywf ] |> Async.Parallel |> Async.RunSynchronously
收益率[|()|]
如预期的那样。
在对上述构图有了清晰的理解之后,它的行为符合预期,但对于证明我们确实构建了一个成熟的异步工作流程来说太简单了,让我们尝试一些仍然简单但更令人印象深刻的方法。让我们实现一个类型int -> Async<unit>
的函数,该函数采用int
参数,进行非阻塞暂停,返回相同的unit
,并作为副作用打印参数的值:
let wasteSomeTime x =
async {
do! Async.Sleep(100)
printfn "%d" x
}
现在,让我们使用wasteSomeTime
来编写具有不同参数的异步计算序列,并在类似于前一种情况下的组合中对其进行评估:
let wasteful = [1..10] |> Seq.map wasteSomeTime // binds to seq<Async<unit>>
wasteful |> Async.Parallel |> Async.RunSynchronously |> ignore
输出将类似于
>
4
2
3
6
8
10
7
5
9
1
val it : unit = ()
这确实证明了异步执行。
在摸索了上面小型探索会议的点点滴滴之后,我相信您将能够自己回答您的原始问题。