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

Asynchronous 绑定/限制并发作业,而不为每个作业创建线程,asynchronous,concurrency,f#,Asynchronous,Concurrency,F#,我想同时处理io绑定作业的集合,但绑定/限制未完成(正在运行)的并发作业的数量 分块是提高并发性的一种简单方法,但如果项目花费的时间不同,就会产生瓶颈 我发现这样做的方法存在一些问题1)。有没有一种方法可以避免下面的问题,同时保持相对的惯用和简洁 1)使用BlockingCollection(如下所示)。然而,这导致了一种解决方案,其中并发性是由boundedSize数量的“使用者”线程生成的。我正在寻找一种解决方案,它不需要boundedSize线程数来实现boundedSize并发作业。(如

我想同时处理io绑定作业的集合,但绑定/限制未完成(正在运行)的并发作业的数量

分块是提高并发性的一种简单方法,但如果项目花费的时间不同,就会产生瓶颈

我发现这样做的方法存在一些问题
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
类型JobNum=int
让RunConcurrentlyBounded(boundedSize:int)(开始:JobNum)(完成:JobNum)(mkJob:JobNum->Async)=
//创建BlockingCollection
使用bc=新BlockingCollection(boundedSize)
//将异步作业置于BlockingCollection上
Async.Start(异步{
{开始..完成}
|>Seq.map mkJob
|>序号iter bc.添加
bc.CompleteAdding()
})
//每个使用者同步运行其作业
让mkConsumer(consumerId:int)=异步{for bc.getconsumineGenumerable()do!job}
//创建“boundedSize”并行的使用者数量
{1..boundedSize}
|>Seq.map mkConsumer
|>异步并行
|>异步运行
|>忽略
let Test()=
设boundedSize=15
让我们开始=1
让结束=50
让mkJob=(fun jobNum->async{
printfn“%A已启动”作业编号
do!Async.Sleep(5000)
printfn“%A已完成”作业编号
})
RunConcurrentlyBoundedSize开始完成mkJob
我知道TPL和邮箱处理器,但我认为可能有一些简单而健壮的东西,但避免了大量的线程创建路径


理想情况下,只有一个生产者线程和一个消费者线程;我怀疑BlockingCollection可能不是这种情况下正确的并发原语?

这似乎是我将要得到的,通过使用
信号量lim

我认为底层线程池实际上控制了这里的并发性

let RunConcurrentlySemaphore (boundedSize:int) (start : JobNum) (finish : JobNum) (mkJob: JobNum -> Async<Unit>)  =

    use ss = new SemaphoreSlim(boundedSize);

    { start .. finish } 
      |> Seq.map (mkJob >> fun job -> async { 
          do! Async.AwaitTask(ss.WaitAsync())
          try do! job finally ss.Release() |> ignore
      })
      |> Async.Parallel
      |> Async.RunSynchronously
让运行concurrentlysemaphore(boundedSize:int)(开始:JobNum)(完成:JobNum)(mkJob:JobNum->Async)=
使用ss=新信号量lim(boundedSize);
{开始..完成}
|>Seq.map(mkJob>>趣味作业->异步{
do!Async.waitTask(ss.WaitAsync())
试着去做!作业终于完成了ss.Release()|>忽略
})
|>异步并行
|>异步运行

为什么不是第三方物流?它使用起来很简单。