是否有异步运算符来获取由两个异步值(Async< _>)首先返回的值?
例如,给定两个Async< _>一个A1在1秒后返回,A2在2秒后返回的值,然后我想要A1的结果。
原因是我想为异步序列实现一个交错函数,这样如果有两个异步序列"定义"像这样(空间表示时间与大理石图一样):
S1 = -+-----+------------+----+
S2 = ---+-------+----------+-----+
然后我想生成一个新的异步序列,其行为如下:
S3 = -+-+---+---+--------+-+--+--+
交错S1 S2 = S3
但是两个人这样做,我可能需要一种异步选择运算符来选择选择值。
我认为这就像"选择"在Go中,您可以从两个频道获取第一个可用值。
TPL有一个名为Task.WhenAny的函数 - 我可能需要类似的东西。
答案 0 :(得分:4)
我不认为运营商在F#库中可用。要将此功能与现有操作结合使用,您可以使用Async.StartAsTask
,然后使用现有的Task.WhenAny
运算符。但是,我不确定在取消方面的表现如何。
另一种选择是使用F# Snippets web site上实施的Async.Choose
运算符。这不是特别优雅,但它应该做的伎俩!为了使答案独立,代码附在下面。
/// Creates an asynchronous workflow that non-deterministically returns the
/// result of one of the two specified workflows (the one that completes
/// first). This is similar to Task.WaitAny.
static member Choose(a, b) : Async<'T> =
Async.FromContinuations(fun (cont, econt, ccont) ->
// Results from the two
let result1 = ref (Choice1Of3())
let result2 = ref (Choice1Of3())
let handled = ref false
let lockObj = new obj()
let synchronized f = lock lockObj f
// Called when one of the workflows completes
let complete () =
let op =
synchronized (fun () ->
// If we already handled result (and called continuation)
// then ignore. Otherwise, if the computation succeeds, then
// run the continuation and mark state as handled.
// Only throw if both workflows failed.
match !handled, !result1, !result2 with
| true, _, _ -> ignore
| false, (Choice2Of3 value), _
| false, _, (Choice2Of3 value) ->
handled := true
(fun () -> cont value)
| false, Choice3Of3 e1, Choice3Of3 e2 ->
handled := true;
(fun () ->
econt (new AggregateException
("Both clauses of a choice failed.", [| e1; e2 |])))
| false, Choice1Of3 _, Choice3Of3 _
| false, Choice3Of3 _, Choice1Of3 _
| false, Choice1Of3 _, Choice1Of3 _ -> ignore )
op()
// Run a workflow and write result (or exception to a ref cell
let run resCell workflow = async {
try
let! res = workflow
synchronized (fun () -> resCell := Choice2Of3 res)
with e ->
synchronized (fun () -> resCell := Choice3Of3 e)
complete() }
// Start both work items in thread pool
Async.Start(run result1 a)
Async.Start(run result2 b) )
答案 1 :(得分:3)
关于交错两个异步序列的更具体的问题,我最近开始尝试如何使用Hopac进行choose样式编程。我提出的一种可能的方法是定义一种短暂的事件流。你可以在这里找到实验代码:
如您所见,为事件流定义的操作之一是merge
。您正在寻找的内容在语义上可能略有不同,但使用Hopac风格的替代方案(或并发ML风格的事件)可能很容易实现。