如何确保在继续之前启动Async.StartChild?(How to ensure Async.StartChild is started before continuing?)

编程入门 行业动态 更新时间:2024-10-27 08:35:25
如何确保在继续之前启动Async.StartChild?(How to ensure Async.StartChild is started before continuing?)

我试图等待超时事件。 我在函数startAwaitEventWithTimeout后面抽象它。 目前我的代码看起来像这样(包括一些调试输出消息):

let startAwaitEventWithTimeout timeoutMs event = async { Console.WriteLine("Starting AwaitEvent in eventAwaiter") let! eventWaiter = Async.StartChild(Async.AwaitEvent event, timeoutMs) try Console.WriteLine("Awaiting event in eventAwaiter") let! res = eventWaiter return Ok res with :? TimeoutException -> return Error () } |> Async.StartChild

这是一个测试:

let testEvent = Event<string>() [<EntryPoint>] let run _ = async { Console.WriteLine("Starting event awaiter in main") let! eventAwaiter = testEvent.Publish |> startAwaitEventWithTimeout 1000 Console.WriteLine("Triggering event") testEvent.Trigger "foo" Console.WriteLine("Awaiting event awaiter in main") let! result = eventAwaiter match result with | Ok str -> Console.WriteLine("ok: " + str) | Error () -> Console.WriteLine("TIMEOUT") } |> Async.RunSynchronously 0

不幸的是,尽管我已经看到所有内容都在“等待”,但似乎run函数继续在Async.AwaitEvent有机会订阅事件之前触发事件。 简而言之,这是我得到的输出:

Starting event awaiter in main Starting AwaitEvent in eventAwaiter Triggering event Awaiting event awaiter in main Awaiting event in eventAwaiter TIMEOUT

这是我所期望的:

Starting event awaiter in main Starting AwaitEvent in eventAwaiter Awaiting event in eventAwaiter <-- this is moved up Triggering event Awaiting event awaiter in main ok foo

我可以通过添加例如do! Async.Sleep 100来解决问题do! Async.Sleep 100 调用startAwaitEventWithTimeout和触发事件之间的do! Async.Sleep 100 ,但当然这不太理想。

我做错了什么,有没有办法可以确保在触发事件之前调用AwaitEvent ?

(旁注:我这样做是因为我们通过TCP调用远程进程,所有来自远程的通信都是通过事件完成的。)

I am trying to await an event with timeout. I am abstracting this behind a function startAwaitEventWithTimeout. Currently my code looks like this (including some debug output messages):

let startAwaitEventWithTimeout timeoutMs event = async { Console.WriteLine("Starting AwaitEvent in eventAwaiter") let! eventWaiter = Async.StartChild(Async.AwaitEvent event, timeoutMs) try Console.WriteLine("Awaiting event in eventAwaiter") let! res = eventWaiter return Ok res with :? TimeoutException -> return Error () } |> Async.StartChild

Here's a test:

let testEvent = Event<string>() [<EntryPoint>] let run _ = async { Console.WriteLine("Starting event awaiter in main") let! eventAwaiter = testEvent.Publish |> startAwaitEventWithTimeout 1000 Console.WriteLine("Triggering event") testEvent.Trigger "foo" Console.WriteLine("Awaiting event awaiter in main") let! result = eventAwaiter match result with | Ok str -> Console.WriteLine("ok: " + str) | Error () -> Console.WriteLine("TIMEOUT") } |> Async.RunSynchronously 0

Unfortunately, even though everything is "awaited" as far as I can see, it seems the run function proceeds to triggering the event before Async.AwaitEvent has had a chance to subscribe to the event. In short, here is the output I get:

Starting event awaiter in main Starting AwaitEvent in eventAwaiter Triggering event Awaiting event awaiter in main Awaiting event in eventAwaiter TIMEOUT

Here is what I would expect:

Starting event awaiter in main Starting AwaitEvent in eventAwaiter Awaiting event in eventAwaiter <-- this is moved up Triggering event Awaiting event awaiter in main ok foo

I can work around the problem by adding e.g. do! Async.Sleep 100 between calling startAwaitEventWithTimeout and triggering the event, but of course this is less than ideal.

Have I done something incorrectly, and is there any way I can reliably ensure that AwaitEvent has been called before I trigger the event?

(Side note: I am doing this because we are calling remote processes over TCP, and all communication from the remote is done via events.)

最满意答案

可能我缺少一些要求,但您的代码可以使用continuation轻松重构,并且错误由其自身修复。

let testEvent = Event<unit>() let run _ = let ts = new CancellationTokenSource(TimeSpan.FromSeconds(float 1)) let rc r = Console.WriteLine("ok") let ec _ = Console.WriteLine("exception") let cc _ = Console.WriteLine("cancelled") Async.StartWithContinuations((Async.AwaitEvent testEvent.Publish), rc , ec, cc, ts.Token ) testEvent.Trigger() run()

编辑:如果您有使用异步工作流的特定要求,可以使用TPL中的TaskCompletionSource进行转换。

let registerListener timeout event= let tcs = TaskCompletionSource() let ts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout)) let er _ = tcs.SetResult (Error()) Async.StartWithContinuations(Async.AwaitEvent event, tcs.SetResult << Ok , er , er , ts.Token) Async.AwaitTask tcs.Task let run _ = let testEvent = Event<int>() async { let listener = registerListener (float 1) testEvent.Publish testEvent.Trigger 2 let! ta = listener match ta with | Ok n -> printfn "ok: %d" n | Error () -> printfn "error" } |> Async.RunSynchronously run()

请注意,即使比产生/等待多个子计算更容易理解,但大多数代码仍然是样板,我相信设置简单的超时值必须有更容易的解决方案。

Probably I am missing some requirement but your code can easily be refactored using continuations and the error fixed by itself.

let testEvent = Event<unit>() let run _ = let ts = new CancellationTokenSource(TimeSpan.FromSeconds(float 1)) let rc r = Console.WriteLine("ok") let ec _ = Console.WriteLine("exception") let cc _ = Console.WriteLine("cancelled") Async.StartWithContinuations((Async.AwaitEvent testEvent.Publish), rc , ec, cc, ts.Token ) testEvent.Trigger() run()

Edit: If you have a specific requirement to use async workflows, you can convert it by using TaskCompletionSource in TPL.

let registerListener timeout event= let tcs = TaskCompletionSource() let ts = new CancellationTokenSource(TimeSpan.FromSeconds(timeout)) let er _ = tcs.SetResult (Error()) Async.StartWithContinuations(Async.AwaitEvent event, tcs.SetResult << Ok , er , er , ts.Token) Async.AwaitTask tcs.Task let run _ = let testEvent = Event<int>() async { let listener = registerListener (float 1) testEvent.Publish testEvent.Trigger 2 let! ta = listener match ta with | Ok n -> printfn "ok: %d" n | Error () -> printfn "error" } |> Async.RunSynchronously run()

Note that even though it is far easier to understand than spawning/awaiting multiple child computations, most of this code is still boilerplate and I am sure there must far easier solutions for setting a simple timeout value.

更多推荐

本文发布于:2023-04-29 12:56:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1336259.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:StartChild   Async   ensure   continuing   started

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!