天天看點

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 ;

Erlang入門(二)—并發程式設計

end

    每一個erlang程序都有一個“郵箱”,所有發送到程序的消息都按照到達的順序存儲在“郵箱”裡,上面所示的消息message1,message2,當它們與“郵箱”裡的消息比對,并且限制(guard)通過,那麼相應的actionn将執行,并且receive傳回的是actionn的最後一條執行語句的結果。erlang對“郵箱”裡的消息比對是有選擇性的,隻有比對的消息将被觸發相應的action,而沒有比對的消息将仍然保留在“郵箱”裡。這一機制保證了沒有消息會阻塞其他消息的到達。

    消息到達的順序并不決定消息的優先級,程序将輪流檢查“郵箱”裡的消息進行嘗試比對。消息的優先級别下文再講。

    如何接受特定程序的消息呢?答案很簡單,将發送方(sender)也附送在消息當中,接收方通過模式比對決定是否接受,比如:

pid ! {self(),abc}

給程序pid發送消息{self(),abc},利用self過程得到發送方作為消息發送。然後接收方:

  {pid1,msg} ->

Erlang入門(二)—并發程式設計

通過模式比對決定隻有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).

3> counter:increment(counter1).

increament

4> counter:value(counter1).

1

基于程序的消息傳遞機制可以很容易地實作有限狀态機(fsm),狀态使用函數表示,而事件就是消息。具體不再展開

4.逾時設定

    erlang中的receive文法可以添加一個額外選項:timeout,類似:

   message1 [when guard1] ->

     actions1 ;

   message2 [when guard2] ->

     actions2 ;

Erlang入門(二)—并發程式設計

   after

      timeoutexpr ->

         actionst

after之後的timeoutexpr表達式傳回一個整數time(毫秒級别),時間的精确程度依賴于erlang在作業系統或者硬體的實作。如果在time毫秒内,沒有一個消息被選中,逾時設定将生效,也就是actiont将執行。time有兩個特殊值:

1)infinity(無窮大),infinity是一個atom,指定了逾時設定将永遠不會被執行。

2) 0,逾時如果設定為0意味着逾時設定将立刻執行,但是系統将首先嘗試目前“郵箱”裡的消息。

    逾時的常見幾個應用,比如挂起目前程序多少毫秒:

sleep(time) ->

  receive

    after time ->

    true

end.

    比如清空程序的“郵箱”,丢棄“郵箱”裡的所有消息:

flush_buffer() ->

    anymessage ->

      flush_buffer()

  after 0 ->

    将目前程序永遠挂起:

  suspend() ->

    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

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的模型,書中有一節專門讨論了這一點,着重強調了接口的設計以及抽象層次的隔離問題,不翻譯了

繼續閱讀