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 的时候往信箱里塞一条信息,然后你会看到
所以这时候,信箱的顺序就是错乱的, 那解决办法呢,一种是每次匹配前清理上一条消息,另一种则是加个唯一标识收到消息时匹配下是不是自己要的信息.