F#Async.Parallel |> Async.RunSynchronously仅使用八个CPU内核之一?

时间:2019-08-12 04:56:51

标签: f#

我创建了一个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%。为什么不使用多核?

CPU6IsUsed

1 个答案:

答案 0 :(得分:4)

在开始使任何消息出队之前,您正在同步使所有Add操作入队。这意味着当代理选择下一步做什么时,如果未满,它将始终Add将新项目放入队列。当已满时,它将搜索第一个AsyncGet操作并对其进行处理,但随后会立即(同步)将下一个项目Add放入队列,然后再允许其他消息被出队。这实际上仅允许您一次使一条消息出队,因为代理始终在AddAsyncGet操作之间来回切换。

如果您执行的是AsyncAdd而不是Add,则入队和出队都可以异步进行,您将获得所需的行为,即

let enqueue() = async { for i = 0 to 1000 do do! ag.AsyncAdd (Some i) }