我想每10ms运行一次定期的erlang进程(基于挂钟时间),10ms应该尽可能准确;应该以什么方式实施它?
答案 0 :(得分:6)
如果您想要真正可靠和准确的定期处理,则应使用erlang:monotonic_time/0,1
依赖实际的挂钟时间。如果您使用Stratus3D' answer中的方法,您最终会落后。
start_link(Period) when Period > 0, is_integer(Period) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, Period, []).
...
init(Period) ->
StartT = erlang:monotonic_time(millisecond),
self() ! tick,
{ok, {StartT, Period}}.
...
handle_info(tick, {StartT, Period} = S) ->
Next = Period - (erlang:monotonic_time(millisecond)-StartT) rem Period,
_Timer = erlang:send_after(Next, self(), tick),
do_task(),
{noreply, S}.
你可以在shell中测试:
spawn(fun() ->
P = 1000,
StartT = erlang:monotonic_time(millisecond),
self() ! tick,
(fun F() ->
receive
tick ->
Next = P - (erlang:monotonic_time(millisecond)-StartT) rem P,
erlang:send_after(Next, self(), tick),
io:format("X~n", []),
F()
end
end)()
end).
答案 1 :(得分:0)
如果你真的想要尽可能精确,并且你确定你的任务花费的时间少于你希望它执行的时间间隔,那么你可以有一个长时间运行的过程而不是每10ms生成一个进程。 Erlang可以每10ms生成一个新进程,但除非有理由不能重复使用相同的进程,否则它通常不值得花费(即使它很少)。
我会在OTP gen_server中执行类似的操作:
-module(periodic_task).
... module exports
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
... Rest of API and other OTP callbacks
init([]) ->
Timer = erlang:send_after(0, self(), check),
{ok, Timer}.
handle_info(check, OldTimer) ->
erlang:cancel_timer(OldTimer),
Timer = erlang:send_after(10, self(), check),
do_task(), % A function that executes your task
{noreply, Timer}.
然后像这样启动gen_server:
periodic_task:start_link().
只要gen_server正在运行(如果父进程因为链接而崩溃,那么函数do_task/0
将几乎每10毫秒执行一次。请注意,这不是完全准确的。执行时间会有所不同。实际间隔为10ms +接收定时器消息所需的时间,取消旧定时器,然后启动新定时器。
如果你想每隔10毫秒启动一个单独的进程,你可以让do_task/0
产生一个进程。请注意,这会增加额外的开销,但不一定会使生成之间的间隔不准确。
我的例子来自这个答案:What's the best way to do something periodically in Erlang?