![](/img/trans.png)
[英]F# MailboxProcessor - multiple waiting reader continuations for mailbox
[英]f# mailboxprocessor - replying without waiting for delivery
我正在使用代理 (MailboxProcessor) 在需要响应的地方进行一些有状态处理。
MailboxProcessor.PostAndAsyncReply
发布消息AsyncReplyChannel.Reply
给出响应但是,我通过查看 f# 源代码发现,在传递响应之前,代理不会处理下一条消息。 总的来说,这是一件好事。 但就我而言,代理继续处理消息比等待响应传递更可取。
做这样的事情来提供响应有问题吗? (或者有更好的选择吗?)
async { replyChannel.Reply response } |> Async.Start
我意识到这种方法并不能保证响应会按顺序传递。 我没问题。
参考示例
// agent code
let doWork data =
async { ... ; return response }
let rec loop ( inbox : MailboxProcessor<_> ) =
async {
let! msg = inbox.Receive()
match msg with
| None ->
return ()
| Some ( data, replyChannel ) ->
let! response = doWork data
replyChannel.Reply response (* waits for delivery, vs below *)
// async { replyChannel.Reply response } |> Async.Start
return! loop inbox
}
let agent =
MailboxProcessor.Start(loop)
// caller code
async {
let! response =
agent.PostAndAsyncReply(fun replyChannel -> Some (data, replyChannel))
...
}
FSharp.Control.AsyncSeq在邮箱处理器之上放置了一个更友好的面孔。 异步序列更容易遵循,但是默认实现映射并行具有与所述相同的问题,等待序列中的前一个元素被映射以保留顺序。
所以我创建了一个新函数 taht 只是原始的 AsyncSeq.mapAsyncParallel,经过修改使其不再是真正的映射,因为它是无序的,但它确实映射了所有内容,并且懒惰的 seq 会随着工作的完成而进展。
AsyncSeq.mapAsyncParallelUnordered 的完整源代码
let mapAsyncParallelUnordered (f:'a -> Async<'b>) (s:AsyncSeq<'a>) : AsyncSeq<'b> = asyncSeq {
use mb = MailboxProcessor.Start (fun _ -> async.Return())
let! err =
s
|> AsyncSeq.iterAsyncParallel (fun a -> async {
let! b = f a
mb.Post (Some b) })
|> Async.map (fun _ -> mb.Post None)
|> Async.StartChildAsTask
yield!
AsyncSeq.replicateUntilNoneAsync (Task.chooseTask (err |> Task.taskFault) (async.Delay mb.Receive))
}
下面是我如何在使用 SSLlabs 免费且速度非常慢的 api 的工具中使用它的示例,该 api 很容易过载。 parallelProcessHost
返回一个由 webapi 请求生成的惰性AsyncSeq
,因此AsyncSeq.mapAsyncParallelUnordered AsyncSeq.toListAsync
实际运行请求并允许控制台在输入时打印结果,与发送的顺序无关。
let! es =
hosts
|> Seq.indexed
|> AsyncSeq.ofSeq
|> AsyncSeq.map parallelProcessHost
|> AsyncSeq.mapAsyncParallelUnordered AsyncSeq.toListAsync
|> AsyncSeq.indexed
|> AsyncSeq.map (fun (i, tail) -> (consoleN "-- %d of %i --- %O --" (i+1L) totalHosts (DateTime.UtcNow - startTime)) :: tail )
|> AsyncSeq.collect AsyncSeq.ofSeq
|> AsyncSeq.map stdoutOrStatus //Write out to console
|> AsyncSeq.fold (|||) ErrorStatus.Okay
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.