Concurrency F中agent与任务的组合#

Concurrency F中agent与任务的组合#,concurrency,f#,Concurrency,F#,我在F#中有以下代码,它被认为是足够并发的,可以利用我机器的4个内核。然而,cpu的使用仅限于一个核心 member x.Solve problemDef = use flag = new ManualResetEventSlim(false) let foundSoFar = MSet<'T>() let workPile = MailboxProcessor<seq<'T>>.Start(fun in

我在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<_>
成员x.解决问题def=
使用标志=新的手动重置事件(错误)
让foundSoFar=MSet>.Start(有趣的收件箱->
设剩余=参考0
让rec loop()=异步{
let!data=inbox.Receive()
设data=data |>Seq.filter(非Array.ofSeq
foundSoFar.UnionWith数据
让jobs=ref-1
对于数据中的块|>Seq.distinct |>Seq.chunked 5000 do
Async.Start Array.ofSeq
|>收件箱
}
增加就业
剩余:=!剩余+!作业
如果(!remaining=0&&!jobs=-1),则
flag.Set()|>忽略
其他的
return!loop()
}
循环()
)
workPile.Post problemDef.initData
flag.Wait()|>忽略
foundSoFar:>序号
我将MailboxProcessor用作工作堆,从中获取元素块,通过哈希集过滤它们,并使用结果插入到工作堆中的新元素创建任务。这将重复进行,直到没有新元素生成。此代码的目的是在工作堆中异步插入块,从而使用任务。My问题是没有并行性


编辑:多亏了@jon harrop,我解决了由于seq的懒惰性质而导致的并发问题,并按照建议重新编写了代码。有没有办法摆脱ManualResetEvent,而不使用区分联合作为代理的消息类型(以支持询问消息)?

如果没有完整的示例,我发现很难理解代码的功能(可能是因为它结合了许多不同的并发编程原语,这使得它有点难以理解)

无论如何,
MailboxProcessor
的主体只执行一次(如果您想使用普通代理获得并发性,需要启动多个代理)。在代理主体中,您启动一个任务,为每个
区块运行
problemDef.generators

这意味着
problemDef.generators
应该并行运行。但是调用
foundSoFar.Contains
foundSoFar.UnionWith
以及
Seq.distinct
的代码始终按顺序运行

因此,如果
problemDef.generators
是一个简单而有效的函数,那么跟踪
foundSoFar
(按顺序进行)的开销可能比并行化的开销要大


我不熟悉
MSet,如果没有完整的示例,我会发现很难理解代码的功能(可能是因为它结合了许多不同的并发编程原语,这使得它有点难以理解)

无论如何,
MailboxProcessor
的主体只执行一次(如果您想使用普通代理获得并发性,需要启动多个代理)。在代理主体中,您启动一个任务,为每个
区块运行
problemDef.generators

这意味着
problemDef.generators
应该并行运行。但是调用
foundSoFar.Contains
foundSoFar.UnionWith
以及
Seq.distinct
的代码始终按顺序运行

因此,如果
problemDef.generators
是一个简单而有效的函数,那么跟踪
foundSoFar
(按顺序进行)的开销可能比并行化的开销要大


我不熟悉
MSet您将高级并发原语(任务和代理)与
ManualResetEventSlim
混合使用,这非常糟糕。您可以改用
PostAndReply

您正在使用
Seq
在生成的任务中执行“工作”,该任务是惰性的,因此它在发回后才真正执行任何操作。您可以使用类似
Array.ofSeq
的内容在任务中强制求值吗

您使用
任务的方式不正常。切换到
Async.Start
可能更惯用

没有一个完整的解决方案,我无法验证我的任何猜测

think足够并发以利用4个核


您的多核并行的心智模型可能非常不恰当。

您将高级并发原语(任务和代理)与
ManualResetEventSlim
混合使用,这非常糟糕。您可以改用
PostAndReply

您正在使用
Seq
在生成的任务中执行“工作”,该任务是惰性的,因此它在发回后才真正执行任何操作。您可以使用类似
Array.ofSeq
的内容在任务中强制求值吗

您使用
任务的方式不正常。切换到
Async.Start
可能更惯用

没有一个完整的解决方案,我无法验证我的任何猜测

think足够并发以利用4个核


您对多核并行性的心理模型可能非常不恰当。

我尝试创建的系统由一个代理组成,该代理跟踪找到的元素(在FoundsFar中-这是一个别名为MSet的普通哈希集)以及一些将生成器应用于每个区块并将结果发送回工作堆的映射器。对于映射器,我使用Task.Factory.StartNew创建的任务,而不是使用更多代理和相关开销。我将数据切割为5000个元素的区块,并且我希望每个区块映射之间的并行性。感谢帮助我试图创建的系统由一个代理组成,该代理跟踪找到的元素(在FoundsFar中-这是一个普通的哈希集别名)