f# - Fire and no-wait (without do!) vs Fire and await (do!) 有巨大的差异表现?
问题描述
以下代码需要大约 20 秒才能运行。但是,取消注释后不到一秒钟do!
。为什么会有如此巨大的差异?
更新:
使用时需要 9 秒ag.Add
。我已经更新了代码。
open FSharpx.Control
let test () =
let ag = new BlockingQueueAgent<int option>(500)
let enqueue() = async {
for i = 1 to 500 do
//do! ag.AsyncAdd (Some i) // less than a second with do!
ag.AsyncAdd (Some i) // it takes about 20 seconds without do!
//ag.Add (Some i) // This one takes about 9 seconds
//printfn "=> %d" i
}
async {
do! [ for i = 1 to 100 do yield enqueue() ]
|> Async.Parallel |> Async.Ignore
for i = 1 to 5 do ag.Add None
} |> Async.Start
let rec dequeue() =
async {
let! m = ag.AsyncGet()
match m with
| Some v ->
//printfn "<= %d" v
return! dequeue()
| None ->
printfn "Done"
}
[ for i = 1 to 5 do yield dequeue() ]
|> Async.Parallel |> Async.Ignore |> Async.RunSynchronously
0
解决方案
没有do!
,您不会等待 的结果AsyncAdd
。这意味着AsyncAdd
每次调用enqueue()
. AsyncAdd
虽然如果队列已满,每个调用都会阻塞,但如果你不等待结果,AsyncAdd
那么你的enqueue()
代码不会被阻塞,它会继续启动新的AsyncAdd
操作。
由于您要enqueue()
并行启动 100 个操作,因此可能有多达五万个 AsyncAdd
操作将尝试同时运行,这意味着线程池正在处理 49,500 个阻塞线程。这对你的系统有很多要求。在实践中,您不会同时启动 100 个enqueue()
并行操作,但您将启动与enqueue()
逻辑 CPU 一样多的操作。对于这个答案的其余部分,我将假设您有一个具有超线程的四核处理器(因为您的F# Async.Parallel |> Async.RunSynchronously 只使用八个 CPU 核心之一?问题似乎暗示了),所以这是 8 个逻辑 CPU,所以你将enqueue()
在任何阻塞之前启动 8 个副本,这意味着你AsyncAdd
正在运行的线程,其中 3,500 个将被阻塞。
do!
另一方面,当您使用时,如果AsyncAdd
被阻塞,您的enqueue()
操作也会阻塞,直到队列中有一个空位。因此,一旦队列中有 500 个项目,而不是 (8*500 - 500 = 3500) 个阻塞AsyncAdd
线程位于线程池中,将有 8 个阻塞AsyncAdd
线程(一个用于enqueue()
在您的八个逻辑中的每一个上运行的八个操作中的每个CPU)。八个阻塞线程而不是 3,500 意味着线程池没有进行 3,500 次分配,使用更少的 RAM 和更少的 CPU 时间来处理所有这些线程。
正如我在回答您之前的问题时所说的那样,您似乎确实需要对异步操作有更深入的了解。除了我在该答案中链接的文章(本文和本系列),我还将推荐阅读https://medium.com/jettech/f-async-guide-eb3c8a2d180a这是一个相当长且详细的指南到 F# 异步操作和您可能遇到的一些“陷阱”。我强烈建议您阅读这些文章,然后再回来查看您的问题。随着您从阅读这些文章中获得更深入的理解,您也许能够回答自己的问题!
推荐阅读
- c++ - 从另一个应用程序访问 lua_State
- python - 在python中绘制多个Y轴+“色调”散点图
- javascript - 如何在 onChangeText 回调中立即更改文本?
- java - Spring数据ldap不持久用户但ldap模板有效
- python - 如何跟踪字符串中的符号以进行正确操作?
- java - 如何在testng中将值传递给@test
- java - 如何在 Spring Boot 中禁用某些警告
- maxima - 如何组合两个和并分解出一个共同的元素表达式?
- java - Java右移输出负值
- ios - Xamarin Visual Studio ios 模拟器不工作