Erlang 的并發(fā)編程需要遵循以下基本原則或過程。
列表包括以下原則:
piD = spawn(Fun)
創(chuàng)建一個(gè)評(píng)估 Fun 的新并發(fā)進(jìn)程。新進(jìn)程與調(diào)用方并行運(yùn)行。一個(gè)實(shí)例如下-
-module(helloworld). -export([start/0]). start() -> spawn(fun() -> server("Hello") end). server(Message) -> io:fwrite("~p",[Message]).
上面程序的輸出是-
“Hello”
用標(biāo)識(shí)符 Pid 向進(jìn)程發(fā)送消息。消息發(fā)送是異步的。發(fā)送者不會(huì)等待,而是繼續(xù)它正在做的事情?!埃 环Q為發(fā)送運(yùn)算符。
一個(gè)實(shí)例如下-
-module(helloworld). -export([start/0]). start() -> Pid = spawn(fun() -> server("Hello") end), Pid ! {hello}. server(Message) -> io:fwrite("~p",[Message]).
接收已發(fā)送到進(jìn)程的消息。它具有以下語法-
receive Pattern1 [when Guard1] -> Expressions1; Pattern2 [when Guard2] -> Expressions2; ... End
當(dāng)消息到達(dá)該進(jìn)程時(shí),系統(tǒng)會(huì)嘗試將其與Pattern1匹配(可能有Guard 1)。如果成功,則對(duì)Expressions1求值。如果第一個(gè)模式不匹配,則嘗試使用Pattern2,依此類推。如果沒有任何一個(gè)模式匹配,則保存該消息以供以后處理,然后該過程等待下一條消息。
以下程序顯示了使用全部3個(gè)命令的整個(gè)過程的示例。
-module(helloworld). -export([loop/0,start/0]). loop() -> receive {rectangle, Width, Ht} -> io:fwrite("Area of rectangle is ~p~n" ,[Width * Ht]), loop(); {circle, R} -> io:fwrite("Area of circle is ~p~n" , [3.14159 * R * R]), loop(); Other -> io:fwrite("Unknown"), loop() end. start() -> Pid = spawn(fun() -> loop() end), Pid ! {rectangle, 6, 10}.
關(guān)于上述程序,需要注意以下幾點(diǎn):
loop函數(shù)具有接收端循環(huán)。因此,當(dāng)消息被發(fā)送時(shí),它將被接收端循環(huán)處理。
生成一個(gè)新進(jìn)程,該進(jìn)程將轉(zhuǎn)到循環(huán)函數(shù)。
通過 Pid! message 命令將消息發(fā)送到產(chǎn)生的進(jìn)程。
上面程序的輸出是-
Area of the Rectangle is 60
并發(fā)地,重要的是確定系統(tǒng)上允許的最大進(jìn)程數(shù)。然后,您應(yīng)該能夠了解系統(tǒng)上可以同時(shí)執(zhí)行多少個(gè)進(jìn)程。
讓我們看一個(gè)示例,該示例如何確定系統(tǒng)上可以執(zhí)行的最大進(jìn)程數(shù)。
-module(helloworld). -export([max/1,start/0]). max(N) -> Max = erlang:system_info(process_limit), io:format("Maximum allowed processes:~p~n" ,[Max]), statistics(runtime), statistics(wall_clock), L = for(1, N, fun() -> spawn(fun() -> wait() end) end), {_, Time1} = statistics(runtime), {_, Time2} = statistics(wall_clock), lists:foreach(fun(Pid) -> Pid ! die end, L), U1 = Time1 * 1000 / N, U2 = Time2 * 1000 / N, io:format("Process spawn time=~p (~p) microseconds~n" , [U1, U2]). wait() -> receive die -> void end. for(N, N, F) -> [F()]; for(I, N, F) -> [F()|for(I+1, N, F)]. start()-> max(1000), max(100000).
在任何具有良好處理能力的機(jī)器上,上述兩個(gè)最大函數(shù)都會(huì)通過。下面是上述程序的一個(gè)示例輸出。
Maximum allowed processes:262144 Process spawn time=47.0 (16.0) microseconds Maximum allowed processes:262144 Process spawn time=12.81 (10.15) microseconds
有時(shí),receive語句可能會(huì)永遠(yuǎn)等待一條永遠(yuǎn)不會(huì)出現(xiàn)的消息。這可能有很多原因。例如,我們的程序中可能存在邏輯錯(cuò)誤,或者要向我們發(fā)送消息的進(jìn)程在發(fā)送消息之前可能已經(jīng)崩潰。為了避免這個(gè)問題,我們可以在receive語句中添加一個(gè)超時(shí)。這將設(shè)置進(jìn)程等待接收消息的最長時(shí)間。
以下是指定了超時(shí)的接收消息的語法
receive Pattern1 [when Guard1] -> Expressions1; Pattern2 [when Guard2] -> Expressions2; ... after Time -> Expressions end
最簡單的實(shí)例是創(chuàng)建一個(gè)sleeper函數(shù),如下面的程序所示。
-module(helloworld). -export([sleep/1,start/0]). sleep(T) -> receive after T -> true end. start()-> sleep(1000).
上述代碼在實(shí)際退出之前將休眠1000毫秒。
Erlang中的每個(gè)進(jìn)程都有一個(gè)關(guān)聯(lián)的郵箱。當(dāng)您向該進(jìn)程發(fā)送消息時(shí),該消息將放入郵箱中。僅當(dāng)程序評(píng)估接收語句時(shí),才檢查此郵箱。
以下是“選擇性接收”語句的一般語法。
receive Pattern1 [when Guard1] -> Expressions1; Pattern2 [when Guard1] -> Expressions1; ... after Time -> ExpressionTimeout end
這就是上面的接收語句的工作方式-
當(dāng)我們輸入一個(gè)receive語句時(shí),我們將啟動(dòng)一個(gè)計(jì)時(shí)器(但前提是表達(dá)式中存在一個(gè)after節(jié))。
以郵箱中的第一封郵件,并嘗試使其與Pattern1,Pattern2等匹配。如果匹配成功,將從郵箱中刪除該郵件,并評(píng)估模式后面的表達(dá)式。
如果receive語句中的任何模式都不匹配郵箱中的第一條消息,則將從郵箱中刪除第一條消息并將其放入“保存隊(duì)列”。然后嘗試郵箱中的第二條消息。重復(fù)此過程,直到找到匹配的消息或檢查了郵箱中的所有消息為止。
如果郵箱中的所有郵件都不匹配,則該過程將被掛起,并將在下次將新郵件放入郵箱中時(shí)重新安排執(zhí)行時(shí)間。請(qǐng)注意,當(dāng)收到新消息時(shí),保存隊(duì)列中的消息將不重新匹配;僅匹配新消息。
一旦匹配了一條消息,所有放入保存隊(duì)列的消息就會(huì)按照到達(dá)過程的順序重新輸入到郵箱中。如果設(shè)置了計(jì)時(shí)器,則將其清除。
如果在等待消息時(shí)計(jì)時(shí)器已過去,請(qǐng)?jiān)u估表達(dá)式ExpressionsTimeout并將所有保存的消息按到達(dá)過程的順序放回郵箱。