如何在退出程序之前等待并发 ML 线程完成?
How to wait for Concurrent ML threads to finish before exiting program?
我正在尝试在 MLton 及其并发 ML 实现中实现一个基本的 'stress testing' 程序,特别是描述 here 的 Monte Carlo Pi 测试。虽然我认为我已经了解了我需要的大部分内容,但我有一个问题,我的程序 总是 在 CML 线程完成其工作之前终止。我知道他们正在做某事,因为我有时会看到他们向控制台打印我指示应该打印的文本,但他们开始和 运行 之间似乎存在竞争条件,并且程序作为整个退出。
我启动 CML 的代码是:
local
val iterations : int = 10
val num_threads : int = 1
val still_going : bool ref = ref true
in
val _ = (RunCML.doit ((experiment iterations num_threads still_going), NONE);
(* while !still_going do (); (* Spin-wait for the CML stuff to finish. This doesn't work... *) *)
print "All done!\n")
end
experiment
函数的内容是:
fun experiment (iterations : int) (num_threads : int) (still_going : bool ref) () : unit = let
val iters_per_thread : int = iterations div num_threads
val return_ivars = Vector.tabulate (num_threads, (fn _ => SyncVar.iVar()))
val _ = Vector.map (fn return_ivar => CML.spawn (montecarlopi iters_per_thread return_ivar)) return_ivars
val return_val = Vector.foldl (fn (elem, acc) => acc + (SyncVar.iGet elem)) 0.0 return_ivars
in
(TextIO.print ("Result is: " ^ (Real.toString return_val) ^ "\n");
still_going := false)
end
最后,montecarlopi
函数是:
fun montecarlopi (iterations : int) (return_ivar : real SyncVar.ivar) () = let
val _ = MLton.Random.srand (valOf (MLton.Random.useed ()))
fun helper accumulator 0 = accumulator
| helper accumulator iteration = let
val x : real = wordToBoundedReal (MLton.Random.rand ())
val y : real = wordToBoundedReal (MLton.Random.rand ())
val in_target = (x * x) + (y * y)
val next_iter = iteration - 1
val _ = TextIO.print ("next_iter is: " ^ (Int.toString next_iter) ^ ", in_target is: " ^ (Real.toString in_target) ^ ",x is: " ^ (Real.toString x) ^ ",y is: " ^ (Real.toString y) ^ "\n")
in
if in_target < 1.0 then
helper (accumulator + 1) next_iter
else
helper accumulator next_iter
end
in
SyncVar.iPut (return_ivar, (4.0 * ((real (helper 0 iterations)) / (real iterations))))
end
(可以查看完整(小)程序和随附的 .mlb 文件 here)。我有理由相信 RunCML.doit
函数调用中的位会执行它们应该执行的操作,这使我认为问题 可能 与最外层有关程序的一部分。
如您所见,我尝试旋转等待,使用布尔值上的引用单元来确定何时停止,但这似乎不起作用。尝试使用 RunCML.isRunning
旋转等待也不会 - 尽管这两个听起来都是糟糕的想法,但实际上无论如何。当然,我不能使用 CML 通道或 syncvar 之类的东西,因为它们需要在 RunCML.doit
段内才能使用。更改线程数对这个问题没有任何影响。我也找不到任何其他可以使主要部分进入非阻塞等待的函数。
如何让程序的外部部分等待,直到 RunCML.doit
函数调用内部的大部分程序完成?或者,我是不是在那部分做错了什么,导致了问题?
如果我们查看函数 RunCML.doit
,它的类型 OS.Process.status
可以是 success
或 failure
,您可以从中调用 [=16] =] 返回失败。有一个 CML 函数 shutdown: OS.Process.status -> 'a
.
这可能是失败原因的解释,除非您不调用关机,并且您的部分输出结果永远不会打印。
这里是一个小例子,使用各种 CML 关闭机制,其中 CML 似乎在内部做一些事情,例如 'graceful'。捕获引发的异常并将其转化为失败。
structure Main = struct
open CML
structure RunCML = RunCML;
exception ohno
fun raises() = raise ohno
fun succeed() = RunCML.shutdown(OS.Process.success)
fun fail() = RunCML.shutdown(OS.Process.failure)
fun graceful f () =
let val () = f() handle _ => RunCML.shutdown(OS.Process.failure);
in RunCML.shutdown(OS.Process.success)
end
fun print_status status =
if OS.Process.isSuccess status
then TextIO.print("success\n")
else TextIO.print("failure\n")
fun main() = let
val _ = TextIO.print(banner ^ "\n");
val _ = print_status(RunCML.doit(succeed, NONE))
val _ = print_status(RunCML.doit(fail, NONE))
val _ = print_status(RunCML.doit(raises, NONE))
val _ = print_status(RunCML.doit(graceful(raises), NONE))
val _ = print_status(RunCML.doit(graceful(succeed), NONE))
in OS.Process.success end
end
因此,如果 CML 异常退出,并且您没有自己调用 shutdown,则很有可能某处引发了异常,事实证明确实如此。
将来避免这种静默处理异常的一种方法可能是添加如下内容:
fun noisy f () =
let val () = f()
handle e =>
let val () = TextIO.print ("Exception: " ^ (exnName e)
^ " Message: " ^ (exnMessage e) ^ "\n")
in RunCML.shutdown(OS.Process.failure) end
in RunCML.shutdown(OS.Process.success)
end
然后调用 RunCML.doit(noisy(f), NONE)
P.S。感谢您在您的代码中包含 link,否则将很难理解该问题。
我正在尝试在 MLton 及其并发 ML 实现中实现一个基本的 'stress testing' 程序,特别是描述 here 的 Monte Carlo Pi 测试。虽然我认为我已经了解了我需要的大部分内容,但我有一个问题,我的程序 总是 在 CML 线程完成其工作之前终止。我知道他们正在做某事,因为我有时会看到他们向控制台打印我指示应该打印的文本,但他们开始和 运行 之间似乎存在竞争条件,并且程序作为整个退出。
我启动 CML 的代码是:
local
val iterations : int = 10
val num_threads : int = 1
val still_going : bool ref = ref true
in
val _ = (RunCML.doit ((experiment iterations num_threads still_going), NONE);
(* while !still_going do (); (* Spin-wait for the CML stuff to finish. This doesn't work... *) *)
print "All done!\n")
end
experiment
函数的内容是:
fun experiment (iterations : int) (num_threads : int) (still_going : bool ref) () : unit = let
val iters_per_thread : int = iterations div num_threads
val return_ivars = Vector.tabulate (num_threads, (fn _ => SyncVar.iVar()))
val _ = Vector.map (fn return_ivar => CML.spawn (montecarlopi iters_per_thread return_ivar)) return_ivars
val return_val = Vector.foldl (fn (elem, acc) => acc + (SyncVar.iGet elem)) 0.0 return_ivars
in
(TextIO.print ("Result is: " ^ (Real.toString return_val) ^ "\n");
still_going := false)
end
最后,montecarlopi
函数是:
fun montecarlopi (iterations : int) (return_ivar : real SyncVar.ivar) () = let
val _ = MLton.Random.srand (valOf (MLton.Random.useed ()))
fun helper accumulator 0 = accumulator
| helper accumulator iteration = let
val x : real = wordToBoundedReal (MLton.Random.rand ())
val y : real = wordToBoundedReal (MLton.Random.rand ())
val in_target = (x * x) + (y * y)
val next_iter = iteration - 1
val _ = TextIO.print ("next_iter is: " ^ (Int.toString next_iter) ^ ", in_target is: " ^ (Real.toString in_target) ^ ",x is: " ^ (Real.toString x) ^ ",y is: " ^ (Real.toString y) ^ "\n")
in
if in_target < 1.0 then
helper (accumulator + 1) next_iter
else
helper accumulator next_iter
end
in
SyncVar.iPut (return_ivar, (4.0 * ((real (helper 0 iterations)) / (real iterations))))
end
(可以查看完整(小)程序和随附的 .mlb 文件 here)。我有理由相信 RunCML.doit
函数调用中的位会执行它们应该执行的操作,这使我认为问题 可能 与最外层有关程序的一部分。
如您所见,我尝试旋转等待,使用布尔值上的引用单元来确定何时停止,但这似乎不起作用。尝试使用 RunCML.isRunning
旋转等待也不会 - 尽管这两个听起来都是糟糕的想法,但实际上无论如何。当然,我不能使用 CML 通道或 syncvar 之类的东西,因为它们需要在 RunCML.doit
段内才能使用。更改线程数对这个问题没有任何影响。我也找不到任何其他可以使主要部分进入非阻塞等待的函数。
如何让程序的外部部分等待,直到 RunCML.doit
函数调用内部的大部分程序完成?或者,我是不是在那部分做错了什么,导致了问题?
如果我们查看函数 RunCML.doit
,它的类型 OS.Process.status
可以是 success
或 failure
,您可以从中调用 [=16] =] 返回失败。有一个 CML 函数 shutdown: OS.Process.status -> 'a
.
这可能是失败原因的解释,除非您不调用关机,并且您的部分输出结果永远不会打印。
这里是一个小例子,使用各种 CML 关闭机制,其中 CML 似乎在内部做一些事情,例如 'graceful'。捕获引发的异常并将其转化为失败。
structure Main = struct
open CML
structure RunCML = RunCML;
exception ohno
fun raises() = raise ohno
fun succeed() = RunCML.shutdown(OS.Process.success)
fun fail() = RunCML.shutdown(OS.Process.failure)
fun graceful f () =
let val () = f() handle _ => RunCML.shutdown(OS.Process.failure);
in RunCML.shutdown(OS.Process.success)
end
fun print_status status =
if OS.Process.isSuccess status
then TextIO.print("success\n")
else TextIO.print("failure\n")
fun main() = let
val _ = TextIO.print(banner ^ "\n");
val _ = print_status(RunCML.doit(succeed, NONE))
val _ = print_status(RunCML.doit(fail, NONE))
val _ = print_status(RunCML.doit(raises, NONE))
val _ = print_status(RunCML.doit(graceful(raises), NONE))
val _ = print_status(RunCML.doit(graceful(succeed), NONE))
in OS.Process.success end
end
因此,如果 CML 异常退出,并且您没有自己调用 shutdown,则很有可能某处引发了异常,事实证明确实如此。
将来避免这种静默处理异常的一种方法可能是添加如下内容:
fun noisy f () =
let val () = f()
handle e =>
let val () = TextIO.print ("Exception: " ^ (exnName e)
^ " Message: " ^ (exnMessage e) ^ "\n")
in RunCML.shutdown(OS.Process.failure) end
in RunCML.shutdown(OS.Process.success)
end
然后调用 RunCML.doit(noisy(f), NONE)
P.S。感谢您在您的代码中包含 link,否则将很难理解该问题。