我正在尝试编写一个尝试评估函数的函数,但在特定超时后停止。
我尝试使用Deferred.any
,它返回一个延迟,当底层延迟之一被履行时,它被履行。
type 'a output = OK of 'a | Exn of exn
let fun_test msg f eq (inp,ans) =
let outp = wait_for (Deferred.any
[ return (try OK (f inp) with e -> Exn e)
; (after (Core.Std.sec 0.0) >>| (fun () -> Exn TIMEOUT))])
in {msg = msg;inp = inp;outp = outp;ans = ans;pass = eq outp ans}
我不确定如何从延迟的 monad 中提取值,所以我编写了一个函数“wait_for”,它会一直旋转直到确定基础值。
let rec wait_for x =
match Deferred.peek x with
| None -> wait_for x
| Some done -> done;;
这没有用。在阅读了 Real World OCaml 的 Async 章节后,我意识到我需要启动调度程序。但是我不确定我会Schedule.go
在哪里调用我的代码。我看不到该类型go : ?raise_unhandled_exn:bool -> unit -> Core.Std.never_returns
适合您实际希望异步代码返回的代码的位置。文档说“异步程序在被调用go
之前不会退出。”shutdown
我开始怀疑我对这个问题采取了完全错误的方法,直到我在康奈尔网站上找到了与同一问题非常相似的解决方案
let timeout (thunk:unit -> 'a Deferred.t) (n:float) : ('a option) Deferred.t
= Deferred.any
[ after (sec n) >>| (fun () -> None) ;
thunk () >>= (fun x -> Some x) ]
无论如何,我不太确定我的使用wait_for
是否正确。有没有一种规范的方法可以从延迟的单子中提取一个值?另外我如何启动调度程序?
更新:我尝试仅使用Core.Std.Thread
and编写超时函数Core.Std.Mutex
。
let rec wait_for lck ptr =
Core.Std.Thread.delay 0.25;
Core.Std.Mutex.lock lck;
(match !ptr with
| None -> Core.Std.Mutex.unlock lck; wait_for lck ptr
| Some x -> Core.Std.Mutex.unlock lck; x);;
let timeout t f =
let lck = Core.Std.Mutex.create () in
let ptr = ref None in
let _ = Core.Std.Thread.create
(fun () -> Core.Std.Thread.delay t;
Core.Std.Mutex.lock lck;
(match !ptr with
| None -> ptr := Some (Exn TIMEOUT)
| Some _ -> ());
Core.Std.Mutex.unlock lck;) () in
let _ = Core.Std.Thread.create
(fun () -> let x = f () in
Core.Std.Mutex.lock lck;
(match !ptr with
| None -> ptr := Some x
| Some _ -> ());
Core.Std.Mutex.unlock lck;) () in
wait_for lck ptr
我认为这非常接近工作。它适用于类似的计算let rec loop x = print_string ".\n"; loop x
,但不适用于类似的计算let rec loop x = loop x
。我认为现在的问题是,如果计算f ()
无限循环,那么它的线程永远不会被抢占,所以其他线程都不会注意到超时已经过期。如果线程像打印字符串一样执行 IO,那么线程确实会被抢占。另外我不知道如何杀死一个线程,我在Core.Std.Thread的文档中找不到这样的函数