zoukankan      html  css  js  c++  java
  • 《Erlang程序设计》学习笔记-第2章 并发编程

    http://blog.csdn.net/karl_max/article/details/3977860

    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。

  • 相关阅读:
    吉他 摄影
    前端思考独处时间自我成长
    约束力
    js算法
    旅行计划
    生产者消费者问题
    Lock锁
    线程和进程
    什么是JUC
    GC日志分析和垃圾回收器的新展望
  • 原文地址:https://www.cnblogs.com/fvsfvs123/p/4390558.html
Copyright © 2011-2022 走看看