绑定/限制并发作业,而不创建每个作业的线程

时间:2016-12-21 02:28:32

标签: asynchronous concurrency f#

我想同时处理一组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可能不是这种情况的正确并发原语吗?

1 个答案:

答案 0 :(得分:0)

通过使用dd/mm/yy,这似乎和我一样好。

我认为底层的ThreadPool实际上是在控制并发性。

SemaphoreSlim