zoukankan      html  css  js  c++  java
  • Erlang入门(二)—并发编程

    Erlang中的process——进程是轻量级的,并且进程间无共享。查了很多资料,似乎没人说清楚轻量级进程算是什么概念,继续查找中。。。闲话不提,进入并发编程的世界。本文算是学习笔记,也可以说是《Concurrent Programming in ERLANG》第五张的简略翻译。 
    1.进程的创建
        进程是一种自包含的、分隔的计算单元,并与其他进程并发运行在系统中,在进程间并没有一个继承体系,当然,应用开发者可以设计这样一个继承体系。
        进程的创建使用如下语法:
    Pid = spawn(Module, FunctionName, ArgumentList)

    spawn接受三个参数:模块名,函数名以及参数列表,并返回一个代表创建的进程的标识符(Pid)。
    如果在一个已知进程Pid1中执行:
    Pid2 = spawn(Mod, Func, Args)
    那么,Pid2仅仅能被Pid1可见,Erlang系统的安全性就构建在限制进程扩展的基础上。

    2.进程间通信
        Erlang进程间的通信只能通过发送消息来实现,消息的发送使用!符号:
    Pid ! Message
        其中Pid是接受消息的进程标记符,Message就是消息。接受方和消息可以是任何的有效的Erlang结构,只要他们的结果返回的是进程标记符和消息。
        消息的接受是使用receive关键字,语法如下:
    receive
          Message1 [when Guard1] ->
              Actions1 ;
          Message2 [when Guard2] ->
              Actions2 ;

    end

        每一个Erlang进程都有一个“邮箱”,所有发送到进程的消息都按照到达的顺序存储在“邮箱”里,上面所示的消息Message1,Message2,当它们与“邮箱”里的消息匹配,并且约束(Guard)通过,那么相应的ActionN将执行,并且receive返回的是ActionN的最后一条执行语句的结果。Erlang对“邮箱”里的消息匹配是有选择性的,只有匹配的消息将被触发相应的Action,而没有匹配的消息将仍然保留在“邮箱”里。这一机制保证了没有消息会阻塞其他消息的到达。
        消息到达的顺序并不决定消息的优先级,进程将轮流检查“邮箱”里的消息进行尝试匹配。消息的优先级别下文再讲。

        如何接受特定进程的消息呢?答案很简单,将发送方(sender)也附送在消息当中,接收方通过模式匹配决定是否接受,比如:
    Pid ! {self(),abc}
    给进程Pid发送消息{self(),abc},利用self过程得到发送方作为消息发送。然后接收方:
    receive
      {Pid1,Msg} ->

    end
    通过模式匹配决定只有Pid1进程发送的消息才接受。

    3.一些例子
        仅说明下书中计数的进程例子,我添加了简单注释:
    -module(counter).
    -compile(export_all).
    % start(),返回一个新进程,进程执行函数loop
    start()->spawn(counter, loop,[0]).
    % 调用此操作递增计数
    increment(Counter)->
        Counter!increament.
    % 返回当前计数值
    value(Counter)->
        Counter!{self(),value},
        receive
            {Counter,Value}->
                %返回给调用方
                Value
            end.
      %停止计数      
     stop(Counter)->
         Counter!{self(),stop}.
     loop(Val)->
         receive
             %接受不同的消息,决定返回结果
             increament->
                 loop(Val+1);
             {From,value}->
                 From!{self(),Val},
                 loop(Val);
             stop->
                 true;
             %不是以上3种消息,就继续等待
             Other->
                 loop(Val)
          end.   
                 
                            
            


    调用方式:
    1> Counter1=counter:start().
    <0.30.0>
    2> counter:value(Counter1).
    0
    3> counter:increment(Counter1).
    increament
    4> counter:value(Counter1).
    1

    基于进程的消息传递机制可以很容易地实现有限状态机(FSM),状态使用函数表示,而事件就是消息。具体不再展开

    4.超时设置
        Erlang中的receive语法可以添加一个额外选项:timeout,类似:
    receive
       Message1 [when Guard1] ->
         Actions1 ;
       Message2 [when Guard2] ->
         Actions2 ;
       
       after
          TimeOutExpr ->
             ActionsT
    end

    after之后的TimeOutExpr表达式返回一个整数time(毫秒级别),时间的精确程度依赖于Erlang在操作系统或者硬件的实现。如果在time毫秒内,没有一个消息被选中,超时设置将生效,也就是ActionT将执行。time有两个特殊值:
    1)infinity(无穷大),infinity是一个atom,指定了超时设置将永远不会被执行。
    2) 0,超时如果设定为0意味着超时设置将立刻执行,但是系统将首先尝试当前“邮箱”里的消息。

        超时的常见几个应用,比如挂起当前进程多少毫秒:
    sleep(Time) ->
      receive
        after Time ->
        true
    end.
        比如清空进程的“邮箱”,丢弃“邮箱”里的所有消息:
       
    flush_buffer() ->
      receive
        AnyMessage ->
          flush_buffer()
      after 0 ->
        true
    end.
        将当前进程永远挂起:
      suspend() ->
        receive
        after
            infinity ->
                true
        end.
        超时也可以应用于实现定时器,比如下面这个例子,创建一个进程,这个进程将在设定时间后向自己发送消息:
    -module(timer).
    -export([timeout/2,cancel/1,timer/3]).
    timeout(Time, Alarm) ->
       spawn(timer, timer, [self(),Time,Alarm]).
    cancel(Timer) ->
       Timer ! {self(),cancel}.
    timer(Pid, Time, Alarm) ->
       receive
        {Pid,cancel} ->
           true
       after Time ->
           Pid ! Alarm
    end.

        
    5、注册进程
        为了给进程发送消息,我们需要知道进程的Pid,但是在某些情况下:在一个很大系统里面有很多的全局servers,或者为了安全考虑需要隐藏进程Pid。为了达到可以发送消息给一个不知道Pid的进程的目的,我们提供了注册进程的办法,给进程们注册名字,这些名字必须是atom。
        基本的调用形式:
    register(Name, Pid)
    将Name与进程Pid联系起来

    unregister(Name)
    取消Name与相应进程的对应关系。

    whereis(Name)
    返回Name所关联的进程的Pid,如果没有进程与之关联,就返回atom:undefined

    registered()
    返回当前注册的进程的名字列表

    6.进程的优先级
    设定进程的优先级可以使用BIFs:
    process_flag(priority, Pri) 

    Pri可以是normal、low,默认都是normal
    优先级高的进程将相对低的执行多一点。

    7.进程组(process group)
        所有的ERLANG进程都有一个Pid与一个他们共有的称为Group Leader相关联,当一个新的进程被创建的时候将被加入同一个进程组。最初的系统进程的Group Leader就是它自身,因此它也是所有被创建进程及子进程的Group Leader。这就意味着Erlang的进程被组织为一棵Tree,其中的根节点就是第一个被创建的进程。下面的BIFs被用于操纵进程组:
    group_leader()
    返回执行进程的Group Leader的Pid
    group_leader(Leader, Pid)
    设置进程Pid的Group Leader为进程的Leader 

    8.Erlang的进程模型很容易去构建Client-Server的模型,书中有一节专门讨论了这一点,着重强调了接口的设计以及抽象层次的隔离问题,不翻译了。

     
  • 相关阅读:
    poj 2485 Highways 最小生成树
    hdu 3415 Max Sum of MaxKsubsequence
    poj 3026 Borg Maze
    poj 2823 Sliding Window 单调队列
    poj 1258 AgriNet
    hdu 1045 Fire Net (二分图匹配)
    poj 1789 Truck History MST(最小生成树)
    fafu 1181 割点
    减肥瘦身健康秘方
    人生的问题
  • 原文地址:https://www.cnblogs.com/xuan52rock/p/4597929.html
Copyright © 2011-2022 走看看