在OCaml中编写可中断读者线程的正确方法是什么?具体来说,以下单线程程序可以正常工作(即 Ctrl-C Ctrl-C 立即中断它):
exception SigInt
let _ =
Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ -> raise SigInt));
try output_string stdout (input_line stdin);
with SigInt -> print_endline "SINGLE_SIGINT"
另一方面,以下程序不能用C-c C-c中断:
let _ =
Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ -> raise SigInt));
let go () =
try output_string stdout (input_line stdin);
with SigInt -> print_endline "CHILD_SIGINT" in
try Thread.join (Thread.create go ());
with SigInt -> print_endline "PARENT_SIGINT"
在OCaml中实现可中断读者线程的跨平台方法是什么?。也就是说,我需要对上面的多线程程序进行哪些更改才能使其可中断?
我已经探索了多个假设来理解为什么上面的多线程示例不起作用,但没有一个对我有意义:
也许input_line不可中断?但上面的单线程示例不起作用。
也许Thread.join
阻止了整个过程的信号?但在这种情况下,以下示例也不会中断:
let _ =
Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ -> raise SigInt));
let rec alloc acc =
alloc (1::acc) in
let go () =
try alloc []
with SigInt -> print_endline "CHILD_SIGINT" in
try Thread.join (Thread.create go ());
with SigInt -> print_endline "PARENT_SIGINT"
...但它是:按 Ctrl-C Ctrl-C 立即退出。
也许信号被传递到主线程,主线程在Thread.join
不间断地等待。如果是这样,按 Ctrl-C Ctrl-C 然后 Enter 将打印"PARENT_SIGINT"
。但它没有:它打印"CHILD_SIGINT"
,这意味着信号被路由到子线程并延迟到input_line
完成。但令人惊讶的是,这可行(并打印CHILD_SIGINT
)
let multithreaded_sigmask () =
Sys.set_signal Sys.sigint (Sys.Signal_handle (fun _ -> raise SigInt));
let go () =
try
ignore (Thread.sigmask Unix.SIG_SETMASK []);
output_string stdout (input_line stdin);
with SigInt -> print_endline "CHILD_SIGINT" in
try
ignore (Thread.sigmask Unix.SIG_SETMASK [Sys.sigint]);
Thread.join (Thread.create go ());
with SigInt -> print_endline "PARENT_SIGINT"
...但Windows上没有sigmask。
答案 0 :(得分:1)
两件事正在共同努力,使行为难以理解。第一个是OS信号传递到过程。第二个是OCaml运行时如何将它们传递给应用程序代码。
查看OCaml源代码,其OS信号处理程序通过全局变量简单地记录信号被引发的事实。然后,该标志由OCaml运行时的其他部分轮询,有时可以安全地传递信号。因此,Thread.sigmask控制OS信号可以传递到OCaml运行时的哪个线程。它无法控制您的应用的投放。
挂起信号由caml_process_pending_signals()传递,由caml_enter_blocking_section()和caml_leave_blocking_section()调用。这里没有线程掩码或亲和力......处理全局待处理信号列表的第一个线程就这样做了。
input_line函数轮询操作系统以获取新输入,每次输入都会进入和离开阻塞部分,因此它会频繁轮询信号。
Thread.join进入阻塞部分,然后无限期地阻塞,直到线程完成,然后离开阻塞部分。因此,在等待时,它不会轮询未决信号。
在您的第一个可中断的示例中,如果您实际键入并按Enter键会发生什么? input_line调用是否实际累积输入并返回它?它可能不会...... Thread.join可能拥有阻塞部分并阻止整个进程的输入和信号传递。