在任一时刻,Erlang允许一个模块的两个版本同时运行:当前版和旧版。重新编译某个模块时,任何运行旧版代码的进程都会被终止,当前版成为旧版,新编译的版本则成为当前版。
-module(test).
-export([start/0]).
start() ->
spawn(fun loop/0).
loop() ->
receive
after 5000 ->
io:format("vsn: ~p~n", [1]),
loop()
end.
编译上述模块,并调用test:start().
创建一个进程循环输出。
Eshell V8.3 (abort with ^G)
1> c(test).
{ok,test}
2> test:start().
<0.62.0>
3> vsn: 1
3> vsn: 1
3> vsn: 1
3>
将io:format("vsn: ~p~n", [1]),
改为io:format("vsn: ~p~n", [2]),
,编译该模块,并再次启动一个进程:
3> vsn: 1
3> c(test).
{ok,test}
4> vsn: 1
4> vsn: 1
4> test:start().
<0.69.0>
5> vsn: 1
5> vsn: 2
5> vsn: 1
5> vsn: 2
5> vsn: 1
5> vsn: 2
5> vsn: 1
5> vsn: 2
会发现两个进程分别运行着两个版本(2
为当前版本,1
为旧版本)的内容。再将2
改为3
,重复上述步骤:
5> vsn: 1
5> vsn: 2
5> c(test).
{ok,test}
6> vsn: 2
6> test:start().
<0.76.0>
7> vsn: 2
7> vsn: 3
7> vsn: 2
7> vsn: 3
7> vsn: 2
7> vsn: 3
7> vsn: 2
会发现打印1
的进程挂掉了,现在只剩下打印2
(旧版本)和3
(当前版本)的进程了。
在此,就验证了开头的观点。
为了避免进程因为调用旧版本导致被杀掉,调用时,可以通过全模块调用即test:loop().
是进程一直使用最新版本的代码。这实际上是Erlang的特性动态代码载入,每当调用someModule:someFunction(...)
时,调用的总是最新版模块里的最新版函数,哪怕当代码在模块里运行时重新编译了该模块也是如此。
将上述代码改为:
-module(test).
-export([start/0,loop/0]).
start() ->
spawn(fun loop/0).
loop() ->
receive
after 5000 ->
io:format("vsn: ~p~n", [1]),
test:loop()
end.
编译上述模块,并启动一个进程。然后将1
改为2
之后再编译一次,会发现进程打印的内容从1
变成了2
了,即使用了最新版本的代码。
Eshell V8.3 (abort with ^G)
1> c(test).
{ok,test}
2> test:start().
<0.62.0>
3> vsn: 1
3> vsn: 1
3> vsn: 1
3> c(test).
{ok,test}
4> vsn: 1
4> vsn: 2
4> vsn: 2
4> vsn: 2
注:
使用c(ModuleName).
编译模块时,除了编译模块外,还会清除使用旧版本(编译前的旧版本)代码的进程,并将当前编译得到的版本加载成为当前版本,此时,原来的当前版本成为旧版本。也就是说,通过c(ModuleName)
编译模块后,无需再通过l(ModuleName)
加载模块。
l(ModuleName)
会清除使用旧版本(编译前的旧版本)代码的进程,并将当前编译得到的版本加载成为当前版本。