目前的设置是这样的
array
|> Seq.map (fun item -> async { return f item})
|> Async.Parallel
|> Async.RunSynchronously
问题是,这会导致创建太多线程并定期崩溃应用程序。
如何在这种情况下限制线程数(比如说,Environment.ProcessorCount)?
答案 0 :(得分:3)
如果要并行化采用数组(或任何序列)作为输入的CPU密集型计算,那么最好使用F# PowerPack中的PSeq
模块(这是仅在.NET 4.0上可用)。它提供了许多标准Array.xyz
函数的并行版本。有关详细信息,您还可以查看F# translation个Parallel Programming with .NET个样本。
解决问题的代码比使用工作流程更简单:
array |> PSeq.map f
|> PSeq.toArray
这两个选项之间存在一些差异:
总之,如果您需要异步操作(例如I / O),那么Async
是最佳选择。如果您有大量CPU密集型任务,那么PSeq
可能是更好的选择(在.NET 4.0上)
答案 1 :(得分:2)
以下是一个工作示例,说明如何使用信号量,以Brian的建议精神:
open System
let throttle n fs =
seq { let n = new Threading.Semaphore(n, n)
for f in fs ->
async { let! ok = Async.AwaitWaitHandle(n)
let! result = Async.Catch f
n.Release() |> ignore
return match result with
| Choice1Of2 rslt -> rslt
| Choice2Of2 exn -> raise exn
}
}
let f i = async { printfn "start %d" i
do! Async.Sleep(2000)
}
let fs = Seq.init 10 f
fs |> throttle 2 |> Async.Parallel |> Async.RunSynchronously |> ignore
答案 2 :(得分:1)
你可能会做一些事情。
首先,由于这使用了ThreadPool
,因此您可以使用ThreadPool.SetMaxThreads
。
其次,您可以沿着这些方向引入自己的油门:
let throttle = makeThrottle(8)
array
|> Seq.map (fun item -> async { do! throttle.Wait()
return f item})
|> Async.Parallel
|> Async.RunSynchronously
makeThrottle()
写起来不会太难,但会产生一点同步开销。如果你试图并行化许多你内存不足的东西,那么节流开销可能不是问题。 (如果您需要这种代码的示例,请告诉我。)
最后,如果这真的是崩溃的东西,它闻起来就像你可能做错了什么。 ThreadPool
通常(但并非总是)在管理自身方面做得很好。但在各种情况下,无论如何,设计自己的油门可能对您的应用程序很有价值。