文档中深层处理器的例子有这么一段。
计算 f 本身也可以执行效果。当 f 执行 Yield 时,它的任务被暂停并放入就绪任务队列,然后调度器运行队列中的下一个任务。如果效果是 Fork f,就暂停当前任务,通过尾调用 spawn f 立即运行新任务 f。要注意,先运行新任务只是一个任意的选择。也可以把 f 的任务放进就绪队列,然后立即继续 k。
也许我漏掉了什么简单的东西,但看看这个调度器例子。
type _ Effect.t += Fork : (unit -> unit) -> unit t
| Yield : unit t
(* Round-robin scheduling with concurrency *)
let run (entry : unit -> unit) : unit =
let pending : (int * (int, unit) continuation) option ref =
ref None (* exchanger awaiting a partner *)
in
let ready = Queue.create () in (* ready-task queue *)
let schedule cont arg =
let resume () = continue cont arg in
Queue.push
resume ready
in
let next () =
if Queue.is_empty ready then () (* finished *)
else begin
let resume = Queue.pop ready in
resume ()
end
in
let rec start (work : unit -> unit) : unit =
match work () with
| () -> next ()
| exception err ->
print_endline (Printexc.to_string err);
next ()
| effect Yield, cont -> schedule cont (); next ()
(* How can work be queued at this point? *)
| effect (Fork work), cont -> schedule cont (); start work
| effect (Xchg value), cont ->
begin match !pending with
| Some (other, saved) -> pending := None; schedule saved value; continue cont other
| None -> pending := Some (value, cont); next ()
end
in
start entry
要怎样把 work 放进 ready?
从英语翻译·查看原文In the documentation's deep-handler example, there's this paragraph:
A computation f can itself perform effects. When f performs Yield, its task is paused and placed in the ready-task queue, then the scheduler runs the next queued task. When the effect is Fork f, it pauses the current task and immediately runs the new task f by tail-calling spawn f. Note that picking the new task to run first is an arbitrary decision. Another possible choice would put f's task into the ready queue and immediately continue k instead.
Perhaps I'm overlooking something simple, but consider this scheduler example:
type _ Effect.t += Fork : (unit -> unit) -> unit t
| Yield : unit t
(* Round-robin scheduling with concurrency *)
let run (entry : unit -> unit) : unit =
let pending : (int * (int, unit) continuation) option ref =
ref None (* exchanger awaiting a partner *)
in
let ready = Queue.create () in (* ready-task queue *)
let schedule cont arg =
let resume () = continue cont arg in
Queue.push
resume ready
in
let next () =
if Queue.is_empty ready then () (* finished *)
else begin
let resume = Queue.pop ready in
resume ()
end
in
let rec start (work : unit -> unit) : unit =
match work () with
| () -> next ()
| exception err ->
print_endline (Printexc.to_string err);
next ()
| effect Yield, cont -> schedule cont (); next ()
(* How can work be queued at this point? *)
| effect (Fork work), cont -> schedule cont (); start work
| effect (Xchg value), cont ->
begin match !pending with
| Some (other, saved) -> pending := None; schedule saved value; continue cont other
| None -> pending := Some (value, cont); next ()
end
in
start entry
What would put work onto ready?