Erlang并发编程

1. 并发原语:

(1) Pid = spawn(Fun) %% 创建一个新的并发进程,用于对Fun求值。

(2) Pid ! Message %% !是发送操作符,消息发送是异步的,返回结果是消息本身,所以Pid1!Pid2!...!M可以向多个进程发送消息M。

(3) receive ... end %% 接收一个发送给当前进程的消息,是同步的。语法:

receive

Pattern1 [when Guard1] -> Expressions1;

Pattern2 [when Guard1] -> Expressions2;

...

end

如果没有匹配任何模式也不会抛出异常,消息会留给后续过程来处理,然后等待下一个消息。

2. area_server_final.erl:一个计算面积的程序的客户机和服务器, 其中用start()封装了发启一个服务器进程,用area封装了发起一个远程调用(rpc),用loop实现的服务器循环,以及在发送和接收消息时判断进程ID等,这些方法都很有用。

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

receive

{Pid, Response} -> Response;

end.

 

loop() ->

receive

{From, {rectangle, W, H}} -> 

From ! {self(), W*H},

loop();

{From, {circle, R}} ->

From ! {self(), 3.14159*R*R};

loop();

{From, Other} ->

From ! {self(), {error, Other}},

loop()

end.

在erlang shell中测试之

1> Pid = area_server_final:start().

2> area_server_final:area(Pid, {rectangle, 10, 8}).

3> area_server_final:area(Pid, {circle, 4}).

 

3. erlang:system_info(process_limit).可以获取虚拟机允许的进程上限(一般为32767),可以通过在启动erl时+P N来修改这个值,如:erl +P 500000

 

4. @spec statistics(runtime) -> {总CPU运行时间, 从上次调用以来的CPU运行时间}

   @spec statistics(wall_clock) -> {总的真实消耗时间,从上调用以来的真实消耗时间}

真实时间包括运行过程中读写硬盘等的时间。

   注意:返回的时间*1000后是微秒

 

5. receive的等待超时语法:

receive

Pattern1 [when Guard1] -> Expressions1;

Pattern2 [when Guard1] -> Expressions2;

...

after Time -> %% Time是毫秒

Expressions

end

6. 自定义的sleep,只有超时的receive

sleep(T) ->

receive

after T ->

true

end.

7. 超时为0的receive的妙用(还能想出别的用法吗?)

   (1)清空进程邮箱里的所有消息:

flush_buffer() ->

receive 

_Any -> flush_buffer()

after 0 ->

true

end.

   (2)优先接收alarm消息,如果邮箱里有大量的消息,这一方法效率很低(有其它方法吗?)

   priority_receive() ->

receive 

{alarm, X} ->

{alarm, X}

after 0 ->

receive

Any -> Any

end

end.

8. 如果超时值是原子infinity,则永远不会超时。(这样有什么用呢?不太明白书上所说的情况。)

9. 用receive超时实现一个计时器:

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

10. receive的工作机制(选择性接收)

(1)启动一个计时器(在有after的情况下)。

(2)从进程邮箱中取出第一个消息,进行模式匹配,如果匹配成功,则从邮箱中删除之。

(3)如果没有匹配成功,则把取出的消息放入“保存队列”,然后继续取消息。重复这一步,直到匹配成功,或邮箱为空。

(4)如果没有匹配成功且此时邮箱为空,则挂起进程,等待新消息进入。注意,当有新消息进入时,只对新消息匹配。

(5)如果有一个消息匹配成功,则把保存队列中的消息按时间顺序放入到进程邮箱中。这时,将计时器清空。

(6)如果在等待消息时,计时器触发,则退出等待,进入after表达式,并将保存队列按时间顺序放回到进程邮箱中。

 

11. 注册进程:

register(AnAtom, Pid). %% 如果这个原子已经被注册了,这个调用会失败

unregister(AnAtom). %% 如果一个进程已经死亡,它会被自动取消注册

whereis(AnAtom) -> Pid | undefined

registered() -> [AnAtom:atom()] %% 返回系统中所有注册进程的列表。

进程注册的原子可以像进程Pid一样使用。

 

12. 并发程序模板:

-module(ctemplate).

-compile(export_all).

 

start() -> spawn(fun() -> loop([])) end).

 

rpc(Pid, Request) ->

Pid ! {self(), Request}, 

receive

{Pid, Response} -> Response

end.

 

loop(X) ->

receive 

Any -> io:format("Received:~p~n", [Any]),

loop(X)

end.

 

13. 尾递归技术:

尾递归在编译时可以优化为一个跳转指令,所以可以无限循环下去。尾递归要确保函数F在调用自身之后,不再调用任何东西,也不要在列表或元组的构造器中使用F。

 

14. 想要代码在运行时能够动态更新,要用下面的spawn:

spawn(Mod, FuncName, Args).

其中Args是形如[A1, A2, ..., AN]的参数列表。

显式指明模块,函数和参数列表的形式被称为MFA。

你可能感兴趣的:(Erlang并发编程)