Asynchronous F#事件在异步工作流中不起作用
我想在火灾后回复一位特工。基本上,代理会触发一个事件,然后回复调用者。但是,我要么不断收到超时错误,要么事件无法正确触发。我尝试过执行Post Fire,它停止了超时错误,但事件没有触发Asynchronous F#事件在异步工作流中不起作用,asynchronous,events,f#,agents,mailboxprocessor,Asynchronous,Events,F#,Agents,Mailboxprocessor,我想在火灾后回复一位特工。基本上,代理会触发一个事件,然后回复调用者。但是,我要么不断收到超时错误,要么事件无法正确触发。我尝试过执行Post Fire,它停止了超时错误,但事件没有触发 let evt = new Event<int>() let stream = evt.Publish type Agent<'T> = MailboxProcessor<'T> type Fire = Fire of int let agent = Agent.Star
let evt = new Event<int>()
let stream = evt.Publish
type Agent<'T> = MailboxProcessor<'T>
type Fire = Fire of int
let agent = Agent.Start(fun inbox ->
let rec loop() = async {
let! msg = inbox.Receive()
let (Fire i) = msg
evt.Trigger i }
loop())
let on i fn =
stream
|> Observable.filter (fun x -> x = i)
|> Observable.filter (fun x -> x <> 1)
|> Observable.subscribe (fun x -> fn x)
let rec collatz n =
printfn "%d" n
on n (fun i ->
if (i % 2 = 0) then collatz (i/2)
else collatz (3*n + 1)) |> ignore
agent.Post (Fire n) // this does not work
// evt.Trigger n // this does works
collatz 13
let evt=new Event()
让stream=evt.Publish
类型代理
火灾类型=内部火灾
让agent=agent.Start(有趣的收件箱->
让rec loop()=异步{
let!msg=inbox.Receive()
放(火)=味精
evt.Trigger i}
循环())
让我来看看fn=
流动
|>Observable.filter(乐趣x->x=i)
|>Observable.filter(乐趣x->x 1)
|>Observable.subscribe(乐趣x->fn x)
让我来记录一下=
printfn“%d”n
关于n(乐趣i->
如果(i%2=0),则collatz(i/2)
else-collatz(3*n+1))|>忽略
agent.Post(Fire n)//这不起作用
//evt.Trigger n//这确实有效
科拉茨13
这是一个简单的实验,重复创建一个函数来查找Collatz系列中的下一个数字,然后调用自身返回值,直到它达到1为止
似乎发生的是触发器只触发一次。我尝试尝试了Async.RunSynchronously/Async.Start/StartChild/SynchronizationContext的每一种组合,但没有任何进展。我发现了一个类似于我现在所做的事情,但这对我也没有帮助
编辑
谢谢菲奥多·索金指出我的疏忽。最初的问题仍然存在,我希望触发事件并返回结果,但得到一个超时
let evt = new Event<int>()
let stream = evt.Publish
type Agent<'T> = MailboxProcessor<'T>
type Command =
| Fire of int
| Get of int * AsyncReplyChannel<int>
let agent = Agent.Start(fun inbox ->
let rec loop() = async {
let! msg = inbox.Receive()
match msg with
| Fire i -> evt.Trigger i
| Get (i,ch) ->
evt.Trigger i
ch.Reply(i)
return! loop() }
loop())
let on i fn =
stream
|> Observable.filter (fun x -> x = i)
|> Observable.filter (fun x -> x <> 1)
|> Observable.subscribe (fun x -> fn x)
let rec collatz n =
printfn "%d" n
on n (fun i ->
if (i % 2 = 0) then collatz (i/2)
else collatz (3*n + 1)) |> ignore
agent.PostAndReply (fun ch -> (Get (n, ch))) |> ignore // timeout
agent.PostAndAsyncReply (fun ch -> (Get (n, ch))) |> Async.Ignore |> Async.Start // works but I need the result
agent.PostAndAsyncReply (fun ch -> (Get (n, ch))) |> Async.RunSynchronously |> ignore // timeout
collatz 13
let evt=new Event()
让stream=evt.Publish
类型代理
类型命令=
|内特之火
|获取int*AsyncReplyChannel
让agent=agent.Start(有趣的收件箱->
让rec loop()=异步{
let!msg=inbox.Receive()
配味精
|点火i->evt.触发i
|获取(i,ch)->
evt.触发器i
总答覆(i)
return!loop()}
循环())
让我来看看fn=
流动
|>Observable.filter(乐趣x->x=i)
|>Observable.filter(乐趣x->x 1)
|>Observable.subscribe(乐趣x->fn x)
让我来记录一下=
printfn“%d”n
关于n(乐趣i->
如果(i%2=0),则collatz(i/2)
else-collatz(3*n+1))|>忽略
agent.PostAndReply(fun ch->(Get(n,ch))|>忽略//超时
agent.PostAndAsyncReply(fun ch->(Get(n,ch)))|>Async.Ignore |>Async.Start//可以工作,但我需要结果
agent.PostAndAsyncReply(fun ch->(Get(n,ch))|>Async.RunSynchronously |>ignore//timeout
科拉茨13
您的循环
函数不循环。它接收第一条消息,触发事件,然后。。。出口。永远不要尝试接收第二条消息
您需要使该功能持续工作:处理第一条消息,然后返回接收下一条消息,然后继续接收下一条消息,依此类推。像这样:
let agent = Agent.Start(fun inbox ->
let rec loop() = async {
let! msg = inbox.Receive()
let (Fire i) = msg
evt.Trigger i
return! loop() }
loop())
编辑 既然你的问题已经到了极限,我将在这里回答你的编辑 在第二个代码段中出现超时的原因是代码中存在死锁。让我们追踪执行情况,看看这一点
collatz
调用collatz
调用向代理发送消息collatz
调用collatz
调用向代理发送消息collatz
调用开始等待代理响应evt.Trigger
内。evt.Trigger
调用尚未返回,因此loop
函数尚未递归,因此尚未调用inbox.Receive
函数,因此第二条消息仍在代理队列中等待
因此,您会遇到一个典型的死锁:collatz
正在等待代理接收其消息,但代理正在等待collatz
完成对事件的处理
最简单、最愚蠢的解决方案是异步触发事件:
async { evt.Trigger i } |> Async.Start
这将确保事件处理程序不是“立即”执行,而是异步执行,可能在不同的线程上执行。这将反过来允许代理在继续自己的执行循环之前不等待事件被处理
但是,一般来说,在处理多线程和异步时,不应该直接调用未知代码。代理永远不应该直接调用
evt.Trigger
,或者它无法控制的任何东西,因为代码可能正在等待代理本身(在您的案例中就是这样),从而引入死锁。Grr。当我在实验中复制/粘贴时,我一定错过了这一点。我将尝试看看这是否有帮助,但这最初是在我的代码中。如果没有do,您可能已经有了loop()
代码>做!不应用于递归调用,因为它会泄漏内存。使用返回!相反谢谢你的提示,解决了这个问题!我盯着这个看太久了。不幸的是,它没有解决我原来的问题。查看我的编辑了解详细信息。好的,既然你已经达到了极限,我已经在这里回答了这个问题。不过,在将来,请不要用你的问题创造一个重复的对话。这与SO的目的背道而驰,SO应该是c