并发
Erlang是一门并发编程语言——这意味着在Erlang中可直接对并行活动(进程)进行编程,并且其并行机制是由Erlang而不是宿主操作系统提供的。
为了对一组并行活动进行控制,Erlang提供了多进程原语:spawn用于启动一个并行计算(称为进程);send向一个进程发送一条消息;而receive从一个进程中接收一条消息。
spawn/3启动一个并发进程并返回一个可用于向该进程发送消息或从该进程接收消息的标识符。
Pid!Msg语法用于消息发送。Pid是代表一个进程的身份的表达式或常量。Msg是要向Pid发送的消息。例如:
- Pid ! {a, 12}
表示将消息{a,12}发送至以Pid为标识符的进程(Pid是进程标识符process identifier的缩写)。在发送之前,消息中的所有参数都先被求值,因此:
- foo(12) ! math3:area({square, 5})
表示对foo(12)求值(必须返回一个有效的进程标识符),并对math3:area({square,5})求值,然后将计算结果(即25)作为一条消息发送给进程。send原语两侧表达式的求值顺序是不确定的。
receive原语用于接收消息。receive语法如下:
- receive
- Message1 ->
- ... ;
- Message2 ->
- ... ;
- ...
- end
这表示尝试接收一个由Message1、Message2等模式之一描述的消息。对该原语进行求值的进程将被挂起,直至接收到一个与Message1、Message2等模式匹配的消息。一旦找到一个匹配,即对“->”右侧的代码求值。
接收到消息后,消息接收模式中的所有未绑定变量都被绑定。
receive的返回值是被匹配上的接收选项所对应的语句序列的求值结果。
我们可以简单认为send发生一条消息而receive接收一条消息,然而更准确的描述则是send将一条消息发送至一个进程的邮箱,而receive尝试从当前进程的邮箱中取出一条消息。
receive是有选择性的,也就是说,它从等候接收进程关注的消息队列中取走第一条与消息模式相匹配的消息。如果找不到与接收模式相匹配的消息,则进程继续挂起直至下一条消息到来——未匹配的消息被保存用于后续处理。
一个echo进程
作为一个并发进程的简单示例,我们创建一个echo进程用于原样发回它所接收到的消息。我们假设进程A向echo进程发送消息{A,Msg},则echo进程向A发送一条包含Msg的新消息。如图1.1所示。
图1.1 一个echo进程
在程序1.5中echo:start()创建一个返回任何发送给它的消息的简单进程。
程序 1.5
- -module(echo).
- -export([start/0, loop/0]).
- start() ->
- spawn(echo, loop, []).
- loop() ->
- receive
- {From, Message} ->
- From ! Message,
- loop()
- end.
spawn(echo,loop[])对echo:loop()所表示的函数相对于调用函数并行求值。因此,针对:
- ...
- Id = echo:start(),
- Id ! {self(), hello}
- ...
进行求值将会启动一个并行进程并向该进程发送消息{self(),hello}——self()是用于获取当前进程标识符的BIF。
脚注
[1] | “实现相关”是指如何完成某个具体操作的细节是系统相关的,也不在本书的讨论范畴之内。 |
[2] | F/N标记表示具备N个参数的函数F。 |
[*] | 译者注:在较新版本的Erlang中,该示例的输出为"abc"。当Erlang shell猜测出待打印的列表为字符串时,会尝试以字符串形式输出列表,参见此处。感谢网友孔雀翎指出。 |