我创建了一个dotnet核心应用程序,并运行以下发行版代码。但是,PC的总CPU使用率仅为20%左右,进程dotnet
的运行仅占12%(我有8个逻辑CPU,但我也看不到其中任何一个都使用100%)。 CPU不是代码的瓶颈吗?
open FSharpx.Control
[<EntryPoint>]
let main argv =
let ag = new BlockingQueueAgent<int option>(500)
let enqueue() = async { for i = 0 to 1000 do ag.Add (Some i) }
async {
do! [ for i = 0 to 1000 do yield enqueue() ]
|> Async.Parallel |> Async.Ignore
ag.Add None
} |> Async.Start
let mutable x = 0
let rec dequeue() =
async {
let! m = ag.AsyncGet()
match m with
| Some v ->
//x <- x ^^^ v
for z = 0 to 10000 do x <- x + z
return! dequeue()
| None ->
printfn "Done %d" x
}
[ for i = 0 to 100 do yield dequeue() ]
|> Async.Parallel |> Async.Ignore |> Async.RunSynchronously
0
以下是BlockingQueueAgent
的源代码:
https://github.com/fsprojects/FSharpx.Async/blob/master/src/FSharpx.Async/BlockingQueueAgent.fs
更新:
添加了更复杂的代码(取代了x <- x ^^^ v
)。现在,它大量使用CPU内核。不过仍然是13%。为什么不使用多核?
答案 0 :(得分:4)
在开始使任何消息出队之前,您正在同步使所有Add
操作入队。这意味着当代理选择下一步做什么时,如果未满,它将始终Add
将新项目放入队列。当已满时,它将搜索第一个AsyncGet
操作并对其进行处理,但随后会立即(同步)将下一个项目Add
放入队列,然后再允许其他消息被出队。这实际上仅允许您一次使一条消息出队,因为代理始终在Add
和AsyncGet
操作之间来回切换。
如果您执行的是AsyncAdd
而不是Add
,则入队和出队都可以异步进行,您将获得所需的行为,即
let enqueue() = async { for i = 0 to 1000 do do! ag.AsyncAdd (Some i) }