我正在尝试等待超时的事件。我在函数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
不幸的是,尽管一切都在等待着#34;据我所见,似乎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
和触发事件之间startAwaitEventWithTimeout
,但当然这不太理想。
我做错了什么,有没有办法可以确保在触发事件之前调用AwaitEvent
?
(旁注:我这样做是因为我们通过TCP调用远程进程,并且来自远程的所有通信都是通过事件完成的。)
答案 0 :(得分:2)
可能我缺少一些要求,但您的代码可以使用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()
请注意,尽管比产生/等待多个子计算更容易理解,但大部分代码仍然是样板,我相信设置简单超时值必须有更简单的解决方案。
答案 1 :(得分:0)
我认为您不会遇到竞争条件,因为您在子计算开始之前始终触发事件。让我们改变设置 - 就像你为测试做的那样 - 在开火前加入延迟。
fn select_lines<'a>(pattern: &String, lines: &'a Vec<String>) -> Vec<&'a String> {
let mut selected_lines: Vec<&'a String> = Vec::new();
for line in lines {
if line.contains(pattern) {
selected_lines.push(&line);
}
}
selected_lines
}
如果射击延迟等于超时,则会出现竞争条件。