Concurrency F中agent与任务的组合#
我在F#中有以下代码,它被认为是足够并发的,可以利用我机器的4个内核。然而,cpu的使用仅限于一个核心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
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中-这是一个普通的哈希集别名)