Mat*_*iak 8 multithreading ocaml sleep delay wait
这是OCaml中的简单游戏循环.显示状态,接收输入,并且状态提前.通过将线程延迟每个循环0.025秒,每秒帧数被限制为40.
main.ml:
let rec main (* state *) frame_time =
(* Display state here. *)
Input.get_input ();
(* Advance state by one frame here. *)
(* If less than 25ms have passed, delay until they have. *)
if((Sys.time ()) < (frame_time +. 0.025)) then
Thread.delay ((frame_time +. 0.025) -. (Sys.time ()));
main (* next_state *) (Sys.time ())
;;
let init =
Graphics.open_graph " 800x500";
let start_time = (Sys.time ()) in
main (* start_state *) start_time
;;
Run Code Online (Sandbox Code Playgroud)
对于此示例,该get_input
函数只是将按键打印到窗口.
input.ml:
let get_input () =
let s = Graphics.wait_next_event
[Graphics.Key_pressed] in
if s.Graphics.keypressed then
Graphics.draw_char s.Graphics.key
;;
Run Code Online (Sandbox Code Playgroud)
Makefile易于测试:
main: input.cmo main.cmo
ocamlfind ocamlc -o $@ unix.cma -thread threads.cma graphics.cma $^
main.cmo: main.ml
ocamlfind ocamlc -c $< -thread
input.cmo: input.ml
ocamlfind ocamlc -c $<
Run Code Online (Sandbox Code Playgroud)
这在大多数情况下都有效,但是当按键很快被按下时,程序会因此错误而崩溃:
Fatal error: exception Unix.Unix_error(2, "select", "")
我相信这与它有关Thread.delay
.造成这个问题的原因是什么,实现恒定FPS的最佳方法是什么?
我不确定发生了什么(这取决于Thread.delay的实现,我不知道).但是,错误2是Unix.EAGAIN
,这表示内核资源暂时短缺.顾名思义,你可能只是尝试再次使用Thread.delay.如果我使用try
... with
来捕获Unix.Unix_error异常,我发现除了EAGAIN之外没有其他错误.如果我只是打印一条消息并继续,该程序似乎工作.至少,它继续将字符回显到窗口并且不会崩溃.我在OS X 10.7(Lion)工作.它可能对您有所不同.
编辑
此代码的另一个可能问题是Sys.time()
返回处理器时间,该时间仅在进程进行实际计算时增加.在进程等待输入时,它不会增加.这意味着总是会调用延迟,即使你在按键之间等了很长时间(这让我困惑了一段时间).使用它可能会更好Unix.gettimeofday ()
,它会返回挂钟时间.
编辑2
经过一些研究和测试后,我相信这个Unix.EAGAIN
错误告诉你完全延迟被某些事件打断了.在你的情况下,中断事件是一个角色的到来(我相信).因此,如果您想等待全部时间,则应将单个调用替换Thread.delay()
为循环.
这为您的主要代码提供了以下内容:
let rec main (* state *) frame_time =
(* Display state here. *)
Input.get_input ();
(* Advance state by one frame here. *)
(* If less than 25ms have passed, delay until they have. *)
let rec delay () =
let duration = frame_time +. 0.025 -. Unix.gettimeofday () in
if duration > 0.0 then
try
Thread.delay duration
with Unix.Unix_error (Unix.EAGAIN, _, _) -> delay ()
in
delay ();
main (* next_state *) (Unix.gettimeofday ())
;;
let init =
Graphics.open_graph " 800x500";
let start_time = (Unix.gettimeofday ()) in
main (* start_state *) start_time
;;
Run Code Online (Sandbox Code Playgroud)
(如果使用Unix.select进行延迟,则可以消除对线程的依赖.但是出于其他原因,您可能仍然需要它们.代码看起来相同,除了错误是EINTR而不是EAGAIN.)
归档时间: |
|
查看次数: |
1046 次 |
最近记录: |