好戏开始了
Erlang 的程序由很多process组成,这些process和人很象:
• 都有自己的一份memory,并且不会互相影响
• 如果要影响别人的memory,都必须明确告诉对方,比如发个消息(说句话)啥的
• 当然,说了别人不一定听,还得确认一下,哥们,明白了么?
• 这些process如果挂了,会在最后吼出来:考,老子被零除!啊!
• 这样,其他的process就会知道,哦,谁谁谁挂了。
• 还有,process间还可以达成某总协议,比如说,一方挂了,另一方帮忙处理后事,之类的。
• 归纳一下就是:
• 1. Erlang程序是由很多进程组成,这些进程之间可以互相发消息。(这个进程和传统意义上操作系统里的进程是不一样的,只是概念上差不多)
• 2. 这些消息不一定能收到或者明白,如果要确认这一点的话,必须发送消息之后等回答
• 3. 两个进程可以结对,连接在一起,如果其中一方挂了,另一方会得到死因
Think of people in a room. The people are the processes. The people in the room have individual private memories; this is the state of a process. To change your memory, I talk to you, and you listen. This is sending and receiving messages. We have children; this is spawn. We die; this is a process exit.
这就是Erlang的模型。
和操作系统的进程相比,Erlang的进程
• 创建和销毁都很快
• 进程间发送消息很快
• 在所有操作系统上,这些进程都一样的工作
• 可以有非常大数量的进程,例如几十万,几百万?
• 进程之间没有共享内存,完全互相独立
• 进程间唯一的交互途径就是发送消息
The Concurrency Primitives
• 1. Pid = spawn(Fun).
• 或者 spawn(Module,Fun,Args). 推荐这种方式,动态更新代码有用
• 创建一个和调用者并行的新进程来执行Fun,可以通过返回的Pid来发送消息
• 2. Pid!Message
• 发送一条消息给Pid对应的进程,该语句返回Message本身,因此Pid1!Pid2!Pid3..!Message,就可以将Message发给所有这些进程
• 发送消息的过程是异步的
• 3. receive ... end
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
end
• 当进程收到消息的时候,挨个匹配这些Pattern,如果匹配到就执行,否则匹配下一条,都没匹配到的话,就把消息保存起来
一个简单的例子
-module(area_server0).
-export([loop/0]).
loop() ->
receive
{rectangle, Width, Ht} ->
io:format("Area of rectangle is ~p~n" ,[Width * Ht]),
loop();
{circle, R} ->
io:format("Area of circle is ~p~n" , [3.14159 * R * R]),
loop();
Other ->
io:format("I don't know what the area of a ~p is ~n" ,[Other]),
loop()
end.
• 首先,在shell里起来 Pid = spawn(fun area_server0:loop/0).
• 然后就可以使用了 Pid ! {rectangle, 6, 10}.
Client-Server 结构
• Erlang里大量使用到C/S结构
• 这里的client和server都是erlang里的进程,都很轻量
• 可以在同一台机器也可以分布在不同的机器上
• 是按其职责来区分client和server的,client发送请求给server,然后server处理之后返回
• 和写信一样,要收到回信就要送过去自己的地址,要收到回答的消息,就必须告诉对方自己的地址(Pid)
-module(area_server_final).
-export([start/0, area/2]).
start() -> spawn(fun loop/0). %% 启动
area(Pid, What) ->
rpc(Pid, What).
rpc(Pid, Request) ->
Pid ! {self(), Request}, %% 发送消息给server
receive
{Pid, Response} -> %% 接收server发回的消息,通过Pid来确认
Response
end.
loop() ->
receive
{From, {rectangle, Width, Ht}} ->
From ! {self(), Width * Ht}, %% 返回结果,并加上自己的身份self(),以确认确实是自己返回的
loop();
{From, {circle, R}} ->
From ! {self(), 3.14159 * R * R},
loop();
{From, Other} ->
From ! {self(), {error,Other}},
loop()
end.
erlang 创建进程有多快?
• Erlang的进程数限制,通过erlang:system_info(process_limit)可以得到,一般是32768(32位机器),要调整可以通过命令行
• erl +P 1000000 来设置
• erlang 创建进程非常快,非常非常快,可以自己试试看
• 在我的笔记本上得到的结果,看起来跑个60万没问题的,当然是什么活都没干,呵呵
8> processes:max(500000).
Maximum allowed processes:1000000
Process spawn time=4.32 (6.858) microseconds
ok
9> processes:max(600000).
Maximum allowed processes:1000000
Process spawn time=4.316666666666666 (6.801666666666667) microseconds
ok
10> processes:max(700000).
Maximum allowed processes:1000000
Process spawn time=4.571428571428571 (7.062857142857143) microseconds
• 使用的程序如下:
-module(processes).
-export([max/1]).
%% max(N)
%% Create N processes then destroy them
%% See how much time this takes
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)].
接收消息时的超时
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
after Time -> %% 超时时间,单位millisecond
Expressions
end
• receive 是在等待,因此我们可以轻松实现一个sleep
sleep(T) ->
receive
after T ->
true
end.
• 如果超时时间设置为0,表示马上超时,立即执行,但在此之前仍然会执行前面的pattern match
• 如果超时时间为infinity,表示永远不超时
• 利用receive ... after 可以轻易实现一个timer
-module(stimer).
-export([start/2, cancel/1]).
start(Time, Fun) -> spawn(fun() -> timer(Time, Fun) end).
cancel(Pid) -> Pid ! cancel.
timer(Time, Fun) ->
receive
cancel ->
void
after Time ->
Fun()
end.
接收消息
• 实际上,所有的发送给一个进程的消息都是保存在这个进程的邮箱里,当进程做receive的时候,就从邮箱里去取一条消息
• receive的实际工作过程是这样的
• 1. 执行receive的时候,启动一个timer(当然是设置了time out时间的情况下)
• 2. 从邮箱里取出第一条消息,进行匹配,如果匹配到某个规则,就从邮箱里删除这条消息,然后执行相应的代码
• 3. 如果没有匹配到任何一个pattern,就把消息移动到一个“保存队列”中,然后在取第二条,以此反复
• 4. 如果一条都没匹配上,进程就挂起等待新的消息进来,新消息进来的时候,只会那新到的消息去匹配,在“保存队列”里的消息不会再匹配过
• 5. 一旦有消息成功匹配,将“保存队列”里的消息按原来到达的次序放到邮箱里,清理timer
• 6. 如果在等待消息的过程中,timer到期了,将执行after 下的语句,同时把“保存队列”里的消息按原来顺序放回邮箱
Registered Processes
• 在erlang里,不知道进程的Pid就无法和他通信
• 但是有时需要让别的进程都和自己通信,这时可以使用registered process,实际上就是给自己起个名字,大家都用这个名字来通信
• register(AnAtom, Pid) 如果这个名字已经被注册了,注册将会失败
• unregister(AnAtom) 如果一个进程挂了,将会自动被取消注册
• whereis(AnAtom) -> Pid | undefined
• registered() -> [AnAtom::atom()]
• 例子,实现一个时钟
-module(clock).
-export([start/2, stop/0]).
start(Time, Fun) ->
register(clock, spawn(fun() -> tick(Time, Fun) end)).
stop() -> clock ! stop.
tick(Time, Fun) ->
receive
stop ->
void
after Time ->
Fun(),
tick(Time, Fun)
end.
32> clock:start(1000,fun() -> io:format("Time is ~p~n",[erlang:now()] ) end).
true
Time is {1221,716569,346481}
Time is {1221,716570,350670}
Time is {1221,716571,355489}
Time is {1221,716572,358767}
Time is {1221,716573,362840}
33> clock:stop().
stop
Erlang 的程序由很多process组成,这些process和人很象:
• 都有自己的一份memory,并且不会互相影响
• 如果要影响别人的memory,都必须明确告诉对方,比如发个消息(说句话)啥的
• 当然,说了别人不一定听,还得确认一下,哥们,明白了么?
• 这些process如果挂了,会在最后吼出来:考,老子被零除!啊!
• 这样,其他的process就会知道,哦,谁谁谁挂了。
• 还有,process间还可以达成某总协议,比如说,一方挂了,另一方帮忙处理后事,之类的。
• 归纳一下就是:
• 1. Erlang程序是由很多进程组成,这些进程之间可以互相发消息。(这个进程和传统意义上操作系统里的进程是不一样的,只是概念上差不多)
• 2. 这些消息不一定能收到或者明白,如果要确认这一点的话,必须发送消息之后等回答
• 3. 两个进程可以结对,连接在一起,如果其中一方挂了,另一方会得到死因
Think of people in a room. The people are the processes. The people in the room have individual private memories; this is the state of a process. To change your memory, I talk to you, and you listen. This is sending and receiving messages. We have children; this is spawn. We die; this is a process exit.
这就是Erlang的模型。
和操作系统的进程相比,Erlang的进程
• 创建和销毁都很快
• 进程间发送消息很快
• 在所有操作系统上,这些进程都一样的工作
• 可以有非常大数量的进程,例如几十万,几百万?
• 进程之间没有共享内存,完全互相独立
• 进程间唯一的交互途径就是发送消息
The Concurrency Primitives
• 1. Pid = spawn(Fun).
• 或者 spawn(Module,Fun,Args). 推荐这种方式,动态更新代码有用
• 创建一个和调用者并行的新进程来执行Fun,可以通过返回的Pid来发送消息
• 2. Pid!Message
• 发送一条消息给Pid对应的进程,该语句返回Message本身,因此Pid1!Pid2!Pid3..!Message,就可以将Message发给所有这些进程
• 发送消息的过程是异步的
• 3. receive ... end
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
end
• 当进程收到消息的时候,挨个匹配这些Pattern,如果匹配到就执行,否则匹配下一条,都没匹配到的话,就把消息保存起来
一个简单的例子
-module(area_server0).
-export([loop/0]).
loop() ->
receive
{rectangle, Width, Ht} ->
io:format("Area of rectangle is ~p~n" ,[Width * Ht]),
loop();
{circle, R} ->
io:format("Area of circle is ~p~n" , [3.14159 * R * R]),
loop();
Other ->
io:format("I don't know what the area of a ~p is ~n" ,[Other]),
loop()
end.
• 首先,在shell里起来 Pid = spawn(fun area_server0:loop/0).
• 然后就可以使用了 Pid ! {rectangle, 6, 10}.
Client-Server 结构
• Erlang里大量使用到C/S结构
• 这里的client和server都是erlang里的进程,都很轻量
• 可以在同一台机器也可以分布在不同的机器上
• 是按其职责来区分client和server的,client发送请求给server,然后server处理之后返回
• 和写信一样,要收到回信就要送过去自己的地址,要收到回答的消息,就必须告诉对方自己的地址(Pid)
-module(area_server_final).
-export([start/0, area/2]).
start() -> spawn(fun loop/0). %% 启动
area(Pid, What) ->
rpc(Pid, What).
rpc(Pid, Request) ->
Pid ! {self(), Request}, %% 发送消息给server
receive
{Pid, Response} -> %% 接收server发回的消息,通过Pid来确认
Response
end.
loop() ->
receive
{From, {rectangle, Width, Ht}} ->
From ! {self(), Width * Ht}, %% 返回结果,并加上自己的身份self(),以确认确实是自己返回的
loop();
{From, {circle, R}} ->
From ! {self(), 3.14159 * R * R},
loop();
{From, Other} ->
From ! {self(), {error,Other}},
loop()
end.
erlang 创建进程有多快?
• Erlang的进程数限制,通过erlang:system_info(process_limit)可以得到,一般是32768(32位机器),要调整可以通过命令行
• erl +P 1000000 来设置
• erlang 创建进程非常快,非常非常快,可以自己试试看
• 在我的笔记本上得到的结果,看起来跑个60万没问题的,当然是什么活都没干,呵呵
8> processes:max(500000).
Maximum allowed processes:1000000
Process spawn time=4.32 (6.858) microseconds
ok
9> processes:max(600000).
Maximum allowed processes:1000000
Process spawn time=4.316666666666666 (6.801666666666667) microseconds
ok
10> processes:max(700000).
Maximum allowed processes:1000000
Process spawn time=4.571428571428571 (7.062857142857143) microseconds
• 使用的程序如下:
-module(processes).
-export([max/1]).
%% max(N)
%% Create N processes then destroy them
%% See how much time this takes
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)].
接收消息时的超时
receive
Pattern1 [when Guard1] ->
Expressions1;
Pattern2 [when Guard2] ->
Expressions2;
...
after Time -> %% 超时时间,单位millisecond
Expressions
end
• receive 是在等待,因此我们可以轻松实现一个sleep
sleep(T) ->
receive
after T ->
true
end.
• 如果超时时间设置为0,表示马上超时,立即执行,但在此之前仍然会执行前面的pattern match
• 如果超时时间为infinity,表示永远不超时
• 利用receive ... after 可以轻易实现一个timer
-module(stimer).
-export([start/2, cancel/1]).
start(Time, Fun) -> spawn(fun() -> timer(Time, Fun) end).
cancel(Pid) -> Pid ! cancel.
timer(Time, Fun) ->
receive
cancel ->
void
after Time ->
Fun()
end.
接收消息
• 实际上,所有的发送给一个进程的消息都是保存在这个进程的邮箱里,当进程做receive的时候,就从邮箱里去取一条消息
• receive的实际工作过程是这样的
• 1. 执行receive的时候,启动一个timer(当然是设置了time out时间的情况下)
• 2. 从邮箱里取出第一条消息,进行匹配,如果匹配到某个规则,就从邮箱里删除这条消息,然后执行相应的代码
• 3. 如果没有匹配到任何一个pattern,就把消息移动到一个“保存队列”中,然后在取第二条,以此反复
• 4. 如果一条都没匹配上,进程就挂起等待新的消息进来,新消息进来的时候,只会那新到的消息去匹配,在“保存队列”里的消息不会再匹配过
• 5. 一旦有消息成功匹配,将“保存队列”里的消息按原来到达的次序放到邮箱里,清理timer
• 6. 如果在等待消息的过程中,timer到期了,将执行after 下的语句,同时把“保存队列”里的消息按原来顺序放回邮箱
Registered Processes
• 在erlang里,不知道进程的Pid就无法和他通信
• 但是有时需要让别的进程都和自己通信,这时可以使用registered process,实际上就是给自己起个名字,大家都用这个名字来通信
• register(AnAtom, Pid) 如果这个名字已经被注册了,注册将会失败
• unregister(AnAtom) 如果一个进程挂了,将会自动被取消注册
• whereis(AnAtom) -> Pid | undefined
• registered() -> [AnAtom::atom()]
• 例子,实现一个时钟
-module(clock).
-export([start/2, stop/0]).
start(Time, Fun) ->
register(clock, spawn(fun() -> tick(Time, Fun) end)).
stop() -> clock ! stop.
tick(Time, Fun) ->
receive
stop ->
void
after Time ->
Fun(),
tick(Time, Fun)
end.
32> clock:start(1000,fun() -> io:format("Time is ~p~n",[erlang:now()] ) end).
true
Time is {1221,716569,346481}
Time is {1221,716570,350670}
Time is {1221,716571,355489}
Time is {1221,716572,358767}
Time is {1221,716573,362840}
33> clock:stop().
stop