使用Async.Parallel进行调度

时间:2014-11-19 09:01:11

标签: asynchronous f# f#-async

有没有办法通过引入调度程序来限制/限制Async.Parallel?我正在寻找执行Asq的Seq<' a>并行但不想超过某个小时限制。

我可以使用共享的可变变量,每个Async<' a>检查,但我想尽可能避免这种情况。

1 个答案:

答案 0 :(得分:5)

在封面下,Async.Parallel操作使用标准.NET线程池。所以,你可以配置线程池,但这可能不是一个好主意(你不应该阻塞线程池中的线程)。

如果我想实施一些限制,我可能会为此创建一个F#代理。代理为您提供了一种协调并发的非常简单的方法 - 它可能比使用可变变量(为此目的)更多的代码,但它为您提供了一个很好的抽象:

// We can ask the agent to enqueue a new work item;
// and the agent sends itself a completed notification
type ThrottlingMessage = 
  | Enqueue of Async<unit>
  | Completed

let throttlingAgent limit = MailboxProcessor.Start(fun inbox -> async {
  // The agent body is not executing in parallel, 
  // so we can safely use mutable queue & counter 
  let queue = System.Collections.Generic.Queue<_>()
  let running = ref 0
  while true do
    // Enqueue new work items or decrement the counter
    // of how many tasks are running in the background
    let! msg = inbox.Receive()
    match msg with
    | Completed -> decr running
    | Enqueue w -> queue.Enqueue(w)
    // If we have less than limit & there is some work to
    // do, then start the work in the background!
    while running.Value < limit && queue.Count > 0 do
      let work = queue.Dequeue()
      incr running
      do! 
        // When the work completes, send 'Completed'
        // back to the agent to free a slot
        async { do! work
                inbox.Post(Completed) } 
        |> Async.StartChild
        |> Async.Ignore })

要使用此功能,您可以创建具有指定限制的代理,然后调用Enqueue添加您的工作项:

let w = throttlingAgent 5 
for i in 0 .. 20 do 
  async { printfn "Starting %d" i
          do! Async.Sleep(1000)
          printfn "Done %d" i  }
  |> Enqueue
  |> w.Post

这解决了与你的问题有点不同的问题 - 但它应该显示方向(而不是Completed通知,你可能希望在后台发送一些async每小时指定数量的“代币”。