<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>时间 on cnDenis的笔记</title><link>https://blog.cndenis.com/tags/%E6%97%B6%E9%97%B4.html</link><description>Recent content in 时间 on cnDenis的笔记</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 19 Sep 2014 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.cndenis.com/tags/%E6%97%B6%E9%97%B4/index.xml" rel="self" type="application/rss+xml"/><item><title>【Erlang】Erlang虚拟机的内部时钟</title><link>https://blog.cndenis.com/Erlang/2014/09/internal_time_of_erlang.html</link><pubDate>Fri, 19 Sep 2014 00:00:00 +0000</pubDate><guid>https://blog.cndenis.com/Erlang/2014/09/internal_time_of_erlang.html</guid><description>&lt;p&gt;Erlang虚拟机可视为一个小型操作系统, 有自己的一套时钟系统, 与系统时间并不是完全一致的.&lt;/p&gt;
&lt;p&gt;Erlang虚拟机时钟使用的是 &lt;code&gt;erlang:now()&lt;/code&gt; 获取, 系统时间使用 &lt;code&gt;os:timestamp()&lt;/code&gt; 获取.
格式是一样的三元组: {百万秒, 秒, 微秒}.&lt;/p&gt;
&lt;h3 id="erlang内部时钟的特点"&gt;Erlang内部时钟的特点
&lt;/h3&gt;&lt;p&gt;为什么Erlang需要实现自己的虚拟机时钟呢? 因为系统时钟有时是不可靠的,
可能会因为联网自动对时或是管理员调时钟而发生突变或往回走,
Erlang虚拟机时钟就是为了避免这些问题而设计的, 它有以下几个特点:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;单向: 永远不会往回走&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;时间间隔接近于真实: 如果在两个时间读取虚拟机时钟, 读数为T1和T2,
那么T1-T2的值接近于两个时间点的真实时间间隔.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;自动修正: 如果Erlang发现虚拟机内部时钟与系统时钟读数不一致,
它会自动缓慢地修正内部时钟, 使内部时钟与系统时钟一致.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第二点是说: 如果两次读取系统时钟, 发现时间相差1小时,
不一定是真实时间过了一小时, 可能是时钟被拨快了1小时, 实际上只过了几秒
但Erlang虚拟机时钟则可以保证, 读数相差1小时, 那么实际时间过了&lt;strong&gt;大约&lt;/strong&gt;一小时,
误差不超过1%.&lt;/p&gt;
&lt;p&gt;第三点的实现方法是, Erlang发现内部时间和系统时间差距&amp;quot;较大&amp;quot;的话,
会调节内部时钟走时速度来适应外部时钟. 比方说外部时钟突然快了1分钟,
Erlang会用100分钟来&amp;quot;追赶&amp;quot;外部时钟, 这段时间内Erlang内部时钟走时会快1%.&lt;/p&gt;
&lt;p&gt;Erlang的这套时钟修正机制虽然不是完美的, 但可以避免出现时间跳跃问题,
防止定时器过早或过晚触发.&lt;/p&gt;
&lt;h3 id="erlang内部时钟的应用"&gt;Erlang内部时钟的应用
&lt;/h3&gt;&lt;p&gt;Erlang内部时钟在以下这个几地方使用:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;erlang:now()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;receive...after&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;timer&lt;/code&gt;模块&lt;/li&gt;
&lt;li&gt;&lt;code&gt;erlang:start_timer/3&lt;/code&gt; 和 &lt;code&gt;erlang:send_after/3&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这就是说, 如果你用&lt;code&gt;send_after&lt;/code&gt;设置1小时后给某进程发消息,
想调系统时间让消息快点到是不可能的, 老老实实等1小时吧.&lt;/p&gt;
&lt;h3 id="erlangnow-的特点"&gt;erlang:now() 的特点
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;单向&lt;/li&gt;
&lt;li&gt;间隔真实&lt;/li&gt;
&lt;li&gt;自动修正&lt;/li&gt;
&lt;li&gt;唯一&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;前三点由Erlang的内部时钟机制保证, 不必多说. 最后一点是指, 即使在多进程下,
每次调用&lt;code&gt;erlang:now()&lt;/code&gt;都会获得不同的值.
因此&lt;code&gt;erlang:now()&lt;/code&gt;的返回值也可以拿来当唯一ID用, 不用担心重复.&lt;/p&gt;
&lt;p&gt;不过这个机制也会造成在高频率(每秒大于1百万次)调用时, &lt;code&gt;erlang:now()&lt;/code&gt;的返回值有偏差.
因为它的返回值精度为1微秒, 1微秒调用2次的话, 也要值这个微秒数加2, 显得走快了.&lt;/p&gt;
&lt;p&gt;当系统时间走时正常, 而且调用频率不高时, &lt;code&gt;erlang:now()&lt;/code&gt;显示的时间和系统时间是一致的.&lt;/p&gt;
&lt;h3 id="关闭内部时钟自动修正"&gt;关闭内部时钟自动修正
&lt;/h3&gt;&lt;p&gt;可以在启动erl的时候, 使用+c参数, 关闭内部时钟的自动修正. 这是一般用不上的功能.&lt;/p&gt;
&lt;h3 id="erlangnow-vs-ostimestamp"&gt;erlang:now() vs os:timestamp()
&lt;/h3&gt;&lt;p&gt;os:timestamp()返回的是系统时钟, 调系统时间的时候就会立即改变.
这对于进行时间相关的逻辑的测试时是很有用的.&lt;/p&gt;
&lt;p&gt;erlang:now()在单进程下稍快于os:timestamp(), 但多进程竞争调用会变慢,
可以参考&lt;a class="link" href="http://stackoverflow.com/questions/17399544/erlangnow-0-is-faster-than-ostimestamp-0" target="_blank" rel="noopener"
 &gt;我在stackoverflow上的提问&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;参考: &lt;a class="link" href="http://www.erlang.org/doc/apps/erts/erts.pdf" target="_blank" rel="noopener"
 &gt;Erlang Run-Time System Application (ERTS)&lt;/a&gt; 第1.2节&lt;/p&gt;</description></item><item><title>【Erl代码片段】start_timer/3 发送的消息中的TimerRef的用途</title><link>https://blog.cndenis.com/Erlang/2014/06/usage_of_ref_in_start_timer.html</link><pubDate>Fri, 20 Jun 2014 00:00:00 +0000</pubDate><guid>https://blog.cndenis.com/Erlang/2014/06/usage_of_ref_in_start_timer.html</guid><description>&lt;p&gt;Erlang中有两个很相似的延迟发送消息的函数, &lt;code&gt;send_after/3&lt;/code&gt; 和 &lt;code&gt;start_timer/3&lt;/code&gt;,
区别仅在于前者返回&lt;code&gt;Msg&lt;/code&gt;, 后者返回&lt;code&gt;{timeout, TimerRef, Msg}&lt;/code&gt;.
后者的这个 &lt;code&gt;TimerRef&lt;/code&gt; 有什么用呢?&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="http://www.cnblogs.com/me-sa/archive/2012/03/16/erlang-timer.html" target="_blank" rel="noopener"
 &gt;坚强2002的博客&lt;/a&gt;给出了例子:&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;来自代码erl5.8.2\lib\stdlib-1.17.2\src\gen_fsm.erl&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;%% Returns Ref, sends event {timeout,Ref,Msg} after Time
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;%% to the (then) current state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;start_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;start_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;&amp;#39;$gen_timer&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Msg&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;%% Returns Ref, sends Event after Time to the (then) current state.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nf"&gt;send_event_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;start_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;&amp;#39;$gen_event&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;%% Returns the remaing time for the timer if Ref referred to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;%% an active timer/send_event_after, false otherwise.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cancel_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nn"&gt;erlang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nb"&gt;cancel_timer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;false&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;receive&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;RemainingTime&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;RemainingTime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;代码十分清楚, 在取消定时器后, 可以利用这个&lt;code&gt;Ref&lt;/code&gt;把消息队列中未处理的消息也删掉.&lt;/p&gt;
&lt;p&gt;类似的用法在&lt;code&gt;demonitor/1&lt;/code&gt;也可以看到. 取消掉一个monitor, 可以使用类似的方法,
把消息列队中的未处理的&lt;code&gt;'DOWN'&lt;/code&gt;消息删掉:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;demonitor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;MonitorRef&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{_,&lt;/span&gt; &lt;span class="nv"&gt;MonitorRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是这样的代码不用自己写, Erlang自带的 &lt;code&gt;demonitor(MonitorRef, [flush])&lt;/code&gt;
就等价于上面的代码.&lt;/p&gt;
&lt;p&gt;为什么&lt;code&gt;demonitor&lt;/code&gt;提供了&lt;code&gt;flush&lt;/code&gt;参数, 而&lt;code&gt;cancel_timer&lt;/code&gt;没有呢?
因为 &lt;code&gt;monitor/2&lt;/code&gt; 产生的消息只能发给调用它进程,
而 &lt;code&gt;start_timer&lt;/code&gt; 产生的消息可以发给任意进程.
由于Erlang只允许读取本进程的消息队列, 不能干涉别的进程,
所以 &lt;code&gt;cancel_timer&lt;/code&gt; 就不提供 &lt;code&gt;flush&lt;/code&gt; 参数, 避免误解.&lt;/p&gt;
&lt;p&gt;不过, 类似的 &lt;code&gt;unlink/1&lt;/code&gt; 却也没有相应的 &lt;code&gt;flush&lt;/code&gt; 参数可以用, 要自己刷消息队列:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-erlang" data-lang="erlang"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;unlink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;receive&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;&amp;#39;EXIT&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;_}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;呵呵.&lt;/p&gt;</description></item></channel></rss>