簡體   English   中英

在F#中將代理與任務結合

[英]Combining Agents with Tasks in F#

我在F#中有以下代碼,該代碼被認為足以並發利用我的計算機的4個內核。 但是cpu的使用僅限於一個內核。

    member x.Solve problemDef =
        use flag = new ManualResetEventSlim(false)
        let foundSoFar = MSet<'T>()
        let workPile = MailboxProcessor<seq<'T>>.Start(fun inbox ->
            let remaining = ref 0
            let rec loop() = async {
                let! data = inbox.Receive()
                let data = data |> Seq.filter (not << foundSoFar.Contains) |> Array.ofSeq
                foundSoFar.UnionWith data
                let jobs = ref -1
                for chunk in data |> Seq.distinct |> Seq.chunked 5000 do
                    Async.Start <| async {
                        Seq.collect problemDef.generators chunk
                        |> Array.ofSeq
                        |> inbox.Post
                    }
                    incr jobs
                remaining := !remaining + !jobs
                if (!remaining = 0 && !jobs = -1) then
                    flag.Set() |> ignore
                else 
                    return! loop()
            }
            loop()
        )
        workPile.Post problemDef.initData
        flag.Wait() |> ignore
        foundSoFar :> seq<_>

我將MailboxProcessor用作工作堆,從中獲取大塊元素,通過HashSet對其進行過濾,並使用結果插入工作堆的新元素創建任務。 重復此過程,直到沒有新的元素產生為止。 該代碼的目的是異步將塊插入工作堆中,從而使用任務。 我的問題是沒有並行性。

編輯:由於@ jon-harrop,我解決了由於seq的惰性而導致的並發問題,並按照建議重新編寫了代碼。 有什么方法可以擺脫ManualResetEvent,而無需使用區分的聯合作為代理的消息類型(以支持詢問消息)?

沒有完整的示例,我發現很難理解您的代碼的功能(也許是因為它結合了許多不同的並發編程原語,這使其難以理解)。

無論如何, MailboxProcessor的主體僅執行一次(如果要使用純代理獲取並發,則需要啟動多個代理)。 在代理程序主體中,您啟動一​​個任務,該任務為每個chunk運行problemDef.generators

這意味着problemDef.generators應該並行運行。 但是,調用foundSoFar.ContainsfoundSoFar.UnionWith以及Seq.distinct的代碼始終按順序運行。

因此,如果problemDef.generators是一個簡單高效的函數,則跟蹤foundSoFar的開銷(按順序進行)可能比並行化要大。

我不熟悉MSet<'T> ,但是如果它是(或如果替換為)線程安全可變集,則您應該能夠在Task.StartNew中並行運行一些Task.StartNew (並行與其他聯合)。

PS:正如我所說,不運行代碼就很難說,所以我的想法可能是完全錯誤的!

您正在將高級並發原語(任務和代理)與ManualResetEventSlim ,這很糟糕。 您可以改用PostAndReply嗎?

您正在使用Seq在產生的任務中執行“工作”,這是很懶的,因此直到將其回發后它實際上不會做任何事情。 您可以使用Array.ofSeq類的Array.ofSeq強制執行任務內部的評估嗎?

您使用Task是異常的。 切換到Async.Start可能更慣用。

沒有完整的解決方案,我將無法驗證我的任何猜測...

認為足夠並發以利用4個核心

您關於多核並行性的思維模型可能還差得很遠。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM