之前看到 Erlang 中的注冊進程時,對注冊并不理解,主要是不理解注冊的原子的作用域。剛才突然想明白了:
復制代碼 代碼如下:
注冊進程關聯的原子具有全局作用域
也就是說關聯了注冊進程之后的原子可以全局被使用
Erlang 中的并發機制是通過消息郵箱實現的,進程間進行通訊的方式只有消息郵箱,而進程間通訊需要知道進程的進程號,而使用 spawn 產生新進程時會返回新進程的進程號供使用。
一個最簡單的進程間通信的程序如下
復制代碼 代碼如下:
-module(test).
start() ->
spawn(?MODULE, loop, []).
loop() ->
io:format("Waiting for new message.~n"),
receive
M -> io:format("New message: ~p~n", [M])
end,
loop().
在 Erlang Shell 中使用 c(test) 編譯這個模塊,之后就可以簡單的使用了.
在下面的代碼中,語句前面的注釋表示解釋,語句后面的注釋表示輸出值, % => 后面的值表示語句的返回值
復制代碼 代碼如下:
% 編譯這個模塊
c(test).
% 開啟無限循環
Pid = test:start().
% Waiting for new message. % 新進程 spawn 后立刻運行
% => 0.35.0> % 返回新進程的進程號
% 向進程發送消息
Pid ! 'message'.
% New message: message % 接收到消息
% Waiting for new message. % 繼續接收消息
% => message % 語句返回值,而非進程返回消息
為了用戶不用每次都 Pid ! 'message',可以加入一個 call 方法進行包裝一下。
復制代碼 代碼如下:
call(Pid, M) ->
Pid ! M.
這樣就可以使用 test:call(Pid, 'message') 發送消息了。
但是這樣寫還有個明顯的弊端,調用 call 時需要 Pid 參數,但是又不能去掉,因為需要進程號才能通信,所以使用時需要用戶維護一個進程號。
而 Erlang 提供了注冊進程的機制用來把原子關聯到進程中,可以解決這個問題
使用 register(atom, Pid) 可以將 atom 關聯到進程號為 Pid 的進程上,這個原子就
修改上面的 start 函數為
復制代碼 代碼如下:
start() ->
register(testp, spawn(?MODULE, loop, [])).
這樣,新的進程將關聯到原子 testp,此時原子就可以當作 Pid 那樣使用 "消息發送操作符" !
復制代碼 代碼如下:
testp ! 'message'
于是可以修改上面的 call 函數,去掉 Pid 參數,而使用關聯后的原子,這個關聯后的原子不止在模塊內有效,在全局作用域中都是有效的。
復制代碼 代碼如下:
call(M) ->
testp ! M.
因此使用注冊進程修改后的程序如下
復制代碼 代碼如下:
-module(test).
start() ->
register(testp, spawn(?MODULE, loop, [])).
loop() ->
io:format("Waiting for new message.~n"),
receive
M -> io:format("New message: ~p~n", [M])
end,
loop().
call(M) ->
testp ! M.
注冊相關的 BIF
復制代碼 代碼如下:
% 注冊 atom 到 Pid
register(atom, Pid).
% 取消 atom 的注冊
unregister(atom).
% 返回 atom 關聯的進程號,如果未關聯,返回 undefined
whereis(atom).
% 返回系統中所有已注冊的進程名
registered().