并发编程
- 在erlang中,进程是属于程序语言的,而非操作系统的;
- erlang并发特点:
- 创建和销毁进程都非常迅速
- 在两个进程之间收发消息非常迅捷
- 进程在所有操作系统上的行为都相同
- 可以创建大量进程
- 进程本身不共享数据,彼此完全独立
- 进程之间交互的方式只有消息传递
在erlang中不会出现竟态条件,共享内存污染等问题,进程之间交互只需要三个原语:spawn、send和receive。
并发原语
spawn
spawn/1的作用是创建一个新的并发进程,行为是对参数fun进行求值,创建出的进程与原进程并发执行。
Pid = spawn(Fun) 创建一个执行Fun函数的进程
spawn的返回值是新并发进程的进程标识符(Pid) ,用户可以通过进程标识符向该进程发送消息。
send
Pid ! Message 向Pid发送Message消息
向Pid发送消息的操作是异步的,发送者不需要等待结果就可以继续处理自己的事务,而其中的 ! 则是消息发送操作符。(就像大学老师讲课一样,老师讲了之后并不会等待你的回应,而是会继续讲自己的内容)
因为发送消息操作本身的返回值就是消息本身,所以可以用
Pid1 ! Pid2 ! Pid3 ! … ! Message
的方式来实现将一条消息发送给多个进程
receive
receive用于接收一个进程的信息,其语法为:
receive
Pattern1 [when Guard1] ->
Expression1;
Pattern2 [when Guard2] ->
Expression2;
…
end
当有一个消息到达进程时,会尝试按照顺序从Pattern1逐个往下判断是否匹配,如果成功,就对对应的Expression求值,如果都没有就会留到后续处理
客户机/服务器
这部分是erlang的核心内容,在客户/服务器架构下的客户机与服务器是分离的进程,erlang的消息传递被用于客户机与服务器之间的通信。
来构建一个简单的自定义的客户机与服务器。
服务器部分:
首先需要一个无限循环的进程用于接收消息,我们可以定一个尾递归的名字为loop的函数,用于使进程一直处于准备接收消息的状态:
loop() ->
receive
{
hello} ->
io:format("hello"),
loop();
{
hello_name, Name} ->
io:format("~w~n",[Name]),
loop();
_ ->
io:format("error"),
loop()
end.
loop/0只做了一件事:receive接收一个消息,通过模式匹配来实现对不同参数有不同的结果输出,然后通过尾递归来实现一直在线等待接收消息
但是这样有个问题在于:我们实际的服务器一般是谁发来消息,我们就要向谁发出回应,如果按照上面的情况,我们并不知道是谁发来的消息,要做到回应消息,就应该在请求的时候带上一个服务器可以返回消息的地址。(就好比你是一个服务器,别人找你去一个地方玩,如果他不告诉你去哪里,那么就算你准备好出门了,也会变得束手无策)
所以我们应该对loop函数稍加改进,在匹配的时候需要知道是从哪里得到消息的,也需要告诉发送消息的进程,我这条进程把返回值带过来了。
loop() ->
receive
{
PidFrom, {
hello}} ->
PidFrom ! {
self(), 'hello'},
loop();
{
PidFrom,