当前位置: 首页 > 文档资料 > Erlang 中文教程 >

并发( Concurrency)

优质
小牛编辑
124浏览
2023-12-01

Erlang中的并发编程需要具有以下基本原则或过程。

该清单包括以下原则 -

piD = spawn(Fun)

创建一个评估Fun的新并发进程。 新进程与调用者并行运行。 一个例子如下 -

例子 (Example)

-module(helloworld). 
-export([start/0]). 
start() ->
   spawn(fun() -> server("Hello") end). 
server(Message) ->
   io:fwrite("~p",[Message]).

上述计划的输出是 -

输出 (Output)

“Hello”

Pid! 信息

使用标识符Pid向进程发送消息。 消息发送是异步的。 发件人不会等待,而是继续其正在做的事情。 '!' 被称为发送运算符。

一个例子如下 -

例子 (Example)

-module(helloworld). 
-export([start/0]). 
start() -> 
   Pid = spawn(fun() -> server("Hello") end), 
   Pid ! {hello}. 
server(Message) ->
   io:fwrite("~p",[Message]).

Receive…end

接收已发送到进程的消息。 它具有以下语法 -

语法 (Syntax)

receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
End

当消息到达进程时,系统会尝试将其与Pattern1(可能的Guard Guard1)进行匹配; 如果成功,则评估Expressions1。 如果第一个模式不匹配,则尝试Pattern2,依此类推。 如果所有模式都不匹配,则保存该消息以供稍后处理,并且该过程等待下一条消息。

以下程序显示了包含所有3个命令的整个过程的示例。

例子 (Example)

-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}.

关于上述计划需要注意以下事项 -

  • 循环函数具有接收端循环。 因此,当发送消息时,它将由接收端循环处理。

  • 产生了一个进入循环函数的新进程。

  • 消息通过Pid发送到生成的进程! 消息命令。

上述计划的输出是 -

输出 (Output)

Area of the Rectangle is 60

最大进程数

在并发中,确定系统允许的最大进程数非常重要。 然后,您应该能够了解有多少进程可以在系统上并发执行。

让我们看一个如何确定可以在系统上执行的最大进程数的示例。

-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).

在任何具有良好处理能力的机器上,上述两个最大功能都将通过。 以下是上述程序的示例输出。

Maximum allowed processes:262144
Process spawn time=47.0 (16.0) microseconds
Maximum allowed processes:262144
Process spawn time=12.81 (10.15) microseconds

收到超时

有时,receive语句可能永远等待永远不会发出的消息。 这可能有很多原因。 例如,我们的程序中可能存在逻辑错误,或者发送消息的进程在发送消息之前可能已崩溃。 为避免此问题,我们可以在receive语句中添加超时。 这将设置进程等待接收消息的最长时间。

以下是指定超时的接收消息的语法

语法 (Syntax)

receive 
Pattern1 [when Guard1] -> 
Expressions1; 
Pattern2 [when Guard2] ->
Expressions2; 
... 
after Time -> 
Expressions 
end

最简单的例子是创建一个睡眠功能,如下面的程序所示。

例子 (Example)

-module(helloworld). 
-export([sleep/1,start/0]). 
sleep(T) ->
   receive 
   after T -> 
      true 
   end. 
start()->
   sleep(1000).

在实际退出之前,上面的代码将睡眠1000 Ms。

选择性接收

Erlang中的每个进程都有一个关联的邮箱。 向进程发送邮件时,邮件将被放入邮箱。 检查此邮箱的唯一时间是程序评估receive语句。

以下是Selective receive语句的一般语法。

语法 (Syntax)

receive 
Pattern1 [when Guard1] ->
Expressions1; 
Pattern2 [when Guard1] ->
Expressions1; 
... 
after 
Time ->
ExpressionTimeout 
end

这就是上述接收语句的工作原理 -

  • 当我们输入receive语句时,我们启动一个计时器(但仅当表达式中存在after section时)。

  • 获取邮箱中的第一条消息,并尝试将其与Pattern1,Pattern2等匹配。 如果匹配成功,则会从邮箱中删除该邮件,并评估该模式后面的表达式。

  • 如果receive语句中没有任何模式与邮箱中的第一条消息匹配,则第一条消息将从邮箱中删除并放入“保存队列”。然后尝试邮箱中的第二条消息。 重复此过程,直到找到匹配的消息,或者直到检查了邮箱中的所有消息。

  • 如果邮箱中没有任何邮件匹配,则该进程将被挂起,并在下次将新邮件放入邮箱时重新安排执行。 请注意,当新消息到达时,保存队列中的消息不会重新匹配; 只有新消息匹配。

  • 一旦匹配了消息,则已经放入保存队列的所有消息将按照它们到达进程的顺序重新输入到邮箱中。 如果设置了计时器,则清除它。

  • 如果在我们等待消息时计时器过去,则计算ExpressionsTimeout表达式,并按照它们到达进程的顺序将任何已保存的消息放回邮箱。