博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ellang 中进程异步通信中的信箱与保序

Posted on 2013-07-05 09:09  bobolive  阅读(226)  评论(0编辑  收藏  举报

erlang 进程通讯中 执行到 receive 语句时 如果信箱没有消息可以匹配时会暂停等待消息. 

go() ->
    register(echo, spawn(test_pid,loop,[])),
    echo ! {self(), hello},
    receive
        {_Pid,Msg} ->
            io:format("~w~n",[Msg])
    end.
    %%Pid ! stop.

loop() ->
    io:format(" loop start~n", []),
    receive
        {From, Msg} ->
            io:format(" loop : ~w | ~w~n", [From, Msg]), From ! {self(), Msg},
            loop();
        stop  -> io:format(" loop : ~w~n", [stop]), true
        %%    true
    end.

由于信箱是先进先出,向同一个进程发送的消息,处理返回一定是保序的。 但这只限于一直不出错,匹配成功的情况,比如 revecie支持 timeOut, timeOut 后 进程将不在主动响应, 但是还能接收信息.

read(Db, Key) ->
    Db ! {self(), {read, Key}},
    receive
        {read, R} -> {ok, R};
        {error, Reason} -> {error, Reason}
    after 10000 -> {error, timeout}
    end.

db() ->
    receive
        {Pid, {read, 2}} -> Pid ! {read, 2},db();
        {Pid, {read, 3}} -> Pid ! {read, 3},db();
        {Pid, {read, 5}} -> Pid ! {error, "not Reason"},db()
    end.

 

假如 有以下调用(Db 是个查询进程,查询完毕会给当前进程返回查询结果)

read(Db, 1).

read(Db, 2).

//after 1500

read(Db, 6).

如果 read(Db, 1). 触发了timeout,  而且 read(Db, 2). 返回了{read, R}. 那么这时信箱处于阻塞状态,当read(Db,6).执行时, 会从信箱中取出第一条匹配返回. 所以 read(Db, 6).会返回 {ok, 2}.

如果是在终端直接运行测试,可以这样写

read(Db, Key) ->
    Db ! {self(), {read, Key}},
    receive
        {read, R} -> {ok, R};
        {error, Reason} -> {error, Reason}
    after 10000 -> test(self()), {error, timeout}
    end.

db() ->
    receive
        {Pid, {read, 2}} -> Pid ! {read, 2},db();
        {Pid, {read, 3}} -> Pid ! {read, 3},db();
        {Pid, {read, 5}} -> Pid ! {error, "not Reason"},db()
    end.

test(Pid) ->
    Pid ! {read, 2}.

在timeout 的时候往信箱里塞一条信息,然后你会看到

所以这时候,信箱的顺序就是错乱的, 那解决办法呢,一种是每次匹配前清理上一条消息,另一种则是加个唯一标识收到消息时匹配下是不是自己要的信息.