我想同时处理一组io-bound作业,但是限制/限制未完成(主动运行)并发作业的数量。
Chunking是一种增加并发性的简单方法,但如果项目花费不同的时间,则会产生瓶颈。
我发现这样做有一些问题1)
。有没有办法避免下面的问题,同时保持比较惯用和简洁?
1)
使用BlockingCollection(如下所示)。但是,这导致了一种解决方案,其中这里的并发是由boundedSize
个“消费者”线程生成的。我正在寻找一个不需要boundedSize
个线程来实现boundedSize
个并发作业的解决方案。 (如果boundedSize
非常大,该怎么办?)。我没有看到我如何处理项目,处理它,然后信号完成。我只能拿物品...因为我不想一下子翻阅整个清单,消费者需要同步运行它。
type JobNum = int
let RunConcurrentlyBounded (boundedSize:int) (start : JobNum) (finish : JobNum) (mkJob: JobNum -> Async<Unit>) =
// create a BlockingCollection
use bc = new BlockingCollection<Async<Unit>>(boundedSize)
// put async jobs on BlockingCollection
Async.Start(async {
{ start .. finish }
|> Seq.map mkJob
|> Seq.iter bc.Add
bc.CompleteAdding()
})
// each consumer runs it's job synchronously
let mkConsumer (consumerId:int) = async { for job in bc.GetConsumingEnumerable() do do! job }
// create `boundedSize` number of consumers in parallel
{ 1 .. boundedSize }
|> Seq.map mkConsumer
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
let Test () =
let boundedSize = 15
let start = 1
let finish = 50
let mkJob = (fun jobNum -> async {
printfn "%A STARTED" jobNum
do! Async.Sleep(5000)
printfn "%A COMPLETED" jobNum
})
RunConcurrentlyBounded boundedSize start finish mkJob
我知道TPL和邮箱处理器,但认为可能有一些简单的东西。健壮,但避免了大量的线程创建路径。
理想情况下,只有一个生产者线程和一个消费者线程;我怀疑BlockingCollection可能不是这种情况的正确并发原语吗?
答案 0 :(得分:0)
dd/mm/yy
,这似乎和我一样好。
我认为底层的ThreadPool实际上是在控制并发性。
SemaphoreSlim