【Erlang】lists:all/2 和 lists:any/2 对于空列表运算结果是啥?

先说结果, 无论F是什么, 结果都是这样:

    lists:all(F, []) = true
    lists:any(F, []) = false

换成其它语言也一样.

lists:all(F, []) = true

从逻辑上, 假如 A 和 B 是两个列表, 那么:

all(F, A ++ B) = all(F, A) andalso all(F, B)

显然, 如果一个大列表全部都是true, 把大列表分割成两个子列表后, 每个子列表中所有元素也都是true.

Erlang中, A = A ++ [], 所以

all(F, A ++ []) = all(F, A) andalso all …
more ...

【Erlang】Erlang虚拟机的内部时钟

Erlang虚拟机可视为一个小型操作系统, 有自己的一套时钟系统, 与系统时间并不是完全一致的.

Erlang虚拟机时钟使用的是 erlang:now() 获取, 系统时间使用 os:timestamp() 获取. 格式是一样的三元组: {百万秒, 秒, 微秒}.

Erlang内部时钟的特点

为什么Erlang需要实现自己的虚拟机时钟呢? 因为系统时钟有时是不可靠的, 可能会因为联网自动对时或是管理员调时钟而发生突变或往回走, Erlang虚拟机时钟就是为了避免这些问题而设计的, 它有以下几个特点:

  1. 单向: 永远不会往回走

  2. 时间间隔接近于真实: 如果在两个时间读取虚拟机时钟, 读数为T1和T2, 那么T1-T2的值接近于两个时间点的真实时间间隔.

  3. 自动修正: 如果Erlang发现虚拟机内部时钟与系统时钟读数不一致, 它会自动缓慢地修正内部时钟, 使内部时钟与系统时钟一致.

第二点是说: 如果两次读取系统时钟, 发现时间相差1小时, 不一定是真实时间过了一小时, 可能是时钟被拨快了1小时, 实际上只过了几秒 但Erlang虚拟机时钟则可以保证, 读数相差1小时, 那么实际时间过了大约一小时, 误差不超过1%.

第三点的实现方法是, Erlang发现内部时间和系统时间差距"较大"的话, 会调节内部时钟走时速度来适应外部时钟. 比方说外部时钟突然快了1分钟, Erlang会用100分钟来 …

more ...

【Erl代码片段】start_timer/3 发送的消息中的TimerRef的用途

Erlang中有两个很相似的延迟发送消息的函数, send_after/3start_timer/3, 区别仅在于前者返回Msg, 后者返回{timeout, TimerRef, Msg}. 后者的这个 TimerRef 有什么用呢?

坚强2002的博客给出了例子:

来自代码erl5.8.2\lib\stdlib-1.17.2\src\gen_fsm.erl

%% Returns Ref, sends event {timeout,Ref,Msg} after Time
%% to the (then) current state.
start_timer(Time, Msg) ->
    erlang:start_timer(Time, self(), {'$gen_timer', Msg …
more ...

【Erl代码片段】简易的并行版lists:map/2

Joe在书上示范了一个并行化的lists:map:

pmap(F, L) ->
    S = self(),
    %% make_ref() returns a unique reference
    %% we'll match on this later
    Ref = erlang:make_ref(),
    Pids = map(fun(I) ->
                spawn(fun() -> do_f(S, Ref, F, I) end)
                end, L),

    %% gather the results
    gather(Pids, Ref).

do_f(Parent, Ref, F, I) ->
    Parent ! {self(), Ref …
more ...

【Erlang】ETS的并发性能调节选项

ETS在创建表时, 有 write_concurrencyread_concurrency 这两个选项, 可以用以优化ETS的并发性能.

write_concurrency 默认设置是 false, 当表进行写操作时, 整个表会被锁起来, 直到写完为止. 把这个值设置为 true, 可以优化并发写性能, 表中的不同项可以同时进行读写, 代价是会消耗一些内存, 并且会降低并发读的性能. 但是对于 order_set 类型的表, 目前的Erlang版本下是不受这个选项影响的.

read_concurrency 默认设置也是 false, 当设置为 true, 并且Erlang运行在多核机器上且开启SMP支持时, 并发读的性能会提升. 代价是读写切换变得更慢.

这两个选项是不会对操作的原子性和独立性有影响的, 也就是说这两项设置只改变性能, 不影响逻辑.

显然, write_concurrency 适用于读少写多的表, read_concurrency 适用于读多写少的表, 以及读写交替少的表 (即读一大堆数据后再写一大堆, 读取操作较少被写入操作打断).

这两个选项是可以同时开启的, 但额外的内存消耗会更大.

测试一下, 分别对默认, 开启read_concurrency, 开启 …

more ...

【Erlang】gen_server terminate的坑

gen_server进程结束时, 会调用terminate函数, 但这并不是在任何情况下都成立的.

当gen_server进程主动关闭时, 也就是在回调函数 handle_xxx 中返回 {stop...} 的时候, terminate 是必然被调用的.

当gen_server进程处于监控树中, 被其监控进程关闭时, 情况就不一样了. 只有在这个进程设置了捕获退出信号, 即 process_flag(trap_exit, true), 并且其开启选项中设置了关闭超时时间, 而不是 brutal_kill, terminate才会被执行.

换句话说, 不捕获退出信号的gen_server被其监控进程关闭时, 会直接死掉, 不执行terminate函数!

看一下 gen_server 的源代码关于消息处理的部分:

decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
    case Msg of
    {system, From, Req …
more ...