在任一时刻,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)会清除使用旧版本(编译前的旧版本)代码的进程,并将当前编译得到的版本加载成为当前版本。
本文介绍了Erlang如何实现代码热更新,允许模块的旧版和新版同时运行。在编译和修改模块后,Erlang会自动终止使用旧版代码的进程,确保新的进程运行最新代码。通过全模块名调用可以确保进程始终使用最新版本的代码,体现了Erlang的动态代码载入特性。
622

被折叠的 条评论
为什么被折叠?



