如何将消息广播到Erlang中的进程列表?控制台挂

时间:2014-10-05 12:28:40

标签: erlang erlang-shell

我是Erlang的新手,我正在尝试了解如何将消息从一个进程发送到进程列表。

据说我们有一个数据结构,其中包含一个包含字符串和Pid的元素的列表。如何让Pid发送消息" M" Pids是前面描述的两个元素之一? 我想出的是:

broadcast(P, M, R) ->
  P ! {self(), friends},
  receive
    {P, Friends} ->
  P ! {self(), {send_message, {M, R, P, Friends}}}
  end.

looper({Name, Friends, Messages}) ->
receive
  {From, friends} ->
    From ! {self(), Friends},
    looper({Name, Friends, Messages});
  {From, {send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}} ->
    if R =< 0 ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});
       R > 0  andalso FriendTale =/= []->
         FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
         looper({Name, FriendTale, [{ID, M} | Messages]})
    end;
   terminate ->
    ok
end.

但据我所知,我并没有正确匹配Pids列表,因此我可以提取&#34;来自Pids列表元素的Pid,或者我没有正确使用该列表向其发送消息。

基本上,我有一个名为&#34; looper&#34;这是不断等待新消息到来。当它收到类型

的消息时
{send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}

其中&#34; M&#34;是我想要广播到名为&#34; Friends&#34;的Pids列表的消息。和R只是一个整数。

R基本上是一个整数,表示消息应该走多远。

e.g. 0 = broadcast the message to self,
     1 = broadcast the message to the friends of the pid,
     2 = broadcast the message to the friends of the friends of the pid and so on...

在我设置Pid后,我从终端获得了什么,设置了#34;友谊&#34; Pids和广播之间的消息是:

1> f().
ok
2> c(facein).
facein.erl:72: Warning: variable 'From' is unused
{ok,facein}
3> {Message, Pid} = facein:start({"dummy", [], []}).
{ok,<0.186.0>}
4> {Message, Pid2} = facein:start({"dummy2", [], []}).
{ok,<0.188.0>}
5> facein:add_friend(Pid,Pid2).
ok
6> facein:broadcast(Pid,"hello",1).    
=ERROR REPORT==== 5-Oct-2014::12:12:58 ===
Error in process <0.186.0> with exit value: {if_clause,[{facein,looper,1,[{file,"facein.erl"},{line,74}]}]}

{<0.177.0>,{send_message,{"hello",1,#Ref<0.0.0.914>}}}

当我查看广播该消息的Pid的消息时,控制台只是挂起而其他Pid没有收到消息。

非常感谢任何帮助。 感谢

2 个答案:

答案 0 :(得分:2)

错误消息

这次你得到的是if_clause错误。在Erlang中,每个表达式都必须返回一些值if。这意味着你可以编写像这样的代码

SomeVar = if 
     R =< 0 ->
       [...]
     R > 0 andalso FriendTale =/= []->
       [...]
  end

如你所知,是否需要&#34;返回&#34;一些东西,要做到这一点,它的一个分支需要运行。或者换句话说,它的一个条款需要马赫。但在您的情况下,当R > 0FriendsTale =:= []不属于它们时。因此出现运行时错误。

由于一般条款的最后一个条款保留为

  _ ->  
     [...]

始终匹配,并避免此类错误。

在您的示例中,您根本不必使用if。你可以做的是延长你收到一些守卫的条款

looper({Name, Friends, Messages}) ->
  receive
    {From, {send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}} 
       when R =< 0 ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});
    {From, {send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}}  
       when R > 0  andalso FriendTale =/= [] ->
          FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
          looper({Name, FriendTale, [{ID, M} | Messages]});
    terminate ->
          ok
  end.

如果收到receive消息,则不必与一个子句匹配。如果没有,它只是留在消息框中(在此接收中被忽略,但可能被另一个接收)。

或者在你的逻辑上,你可以在R本身上模式匹配

looper({Name, Friends, Messages}) ->
  receive
    {From, {send_message, {M, 0, ID, [{FriendPid, FriendName} | FriendTale]}}} ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});
    {From, {send_message, {M, 1, ID, [{FriendPid, FriendName} | FriendTale]}}}  
       when FriendTale =/= [] ->
          FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
          looper({Name, FriendTale, [{ID, M} | Messages]});
    terminate ->
          ok
  end.

为了提高可行性,您可以将R从opque整数更改为miningfull atoms

looper({Name, Friends, Messages}) ->
  receive
    {From, {send_message, {M, back_to_me, ID, [{FriendPid, FriendName} | FriendTale]}}} ->
          From ! {From, {self(), {ID, M}}},
          looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});

    {From, {send_message, {M, to_friends, ID, [{FriendPid, FriendName} | FriendTale]}}}  
       when FriendTale =/= [] ->
          FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
          looper({Name, FriendTale, [{ID, M} | Messages]});

    terminate ->
          ok
  end.

向朋友广播

如果我理解正确looper是代表一个&#34; person&#34;的函数。每个朋友都是存储朋友列表的进程,可以添加和删除,也可以向其他朋友发送消息。

让我们从每个函数的创建子句开始(创建过程接口)

looper(Name, Friends, Messages) ->
  receive 
     {add_friend, Friend} ->
        [...];
     {remove_friend, Friend} ->
        [...];
     {receive_message, Message} ->
        [...];frineds
     {broadcast_to_self, Message} ->
        [...];
     {broadcast_to_friends, Message} ->
        [...];
     terminate ->
        ok
  end

其中大部分都很容易实现,比如

{add_frined, Friend} ->
    looper(Name, [Friend, Friends], Messages);

所以我不会详细介绍。

进行广播的人不会改变状态,所以现在让我们写这样的东西(主要是为了便于阅读)

     {broadcast_to_friends, Message} ->
        handle_broadcast_to_friends(Friends, Message),
        looper(Name, Friends, Messages);

并在

下实现新功能
handle_broadcast_to_friends(Friends, Message) ->
   [ F ! {receive_message, Message} || F <- Friends ].

现在,由于确切地知道要发送哪些原子的元组不方便,我们可以包装我们的&#34;消息接口&#34;进入&#34;功能界面&#34;。例如

add_friend(Person,  Friend) ->
   Person ! {add_friend, Friends}.

receive_message(Person, Message) ->
    Person ! {receive_message, Message}.

我们也可以在你的逻辑实现中使用它们

handle_broadcast_to_friends(Friends, Message) ->
   [ receive_message(F, Message)  || F <- Friends ].

这应该让你开始在正确的轨道上。如果您需要MessageID或类似的东西,只需扩展您的界面即可。如果你真的需要创建broadcast_to_all,你需要考虑如何处理周围的消息,这不是一个简单的问题。

答案 1 :(得分:1)

我建议您首先减少您正在做的事情的复杂性。例如,您收到的此条件处理业务不是您的基本消息传递问题的一部分。以下是常见广播习语的基本示例,使用列表推导发送到函数bcast/2中的pid列表:

-module(bcast).
-export([start/0]).

start() ->
    Pids = [spawn(fun() -> listener() end) || _ <- lists:seq(1,3)],
    Message = "This is my message.",
    ok = bcast(Pids, Message),
    timer:sleep(100), % Give the subordinates time to act.
    [exit(P, kill) || P <- Pids],
    ok.

listener() ->
    receive
        {bcast, Message} ->
            Now = now(),
            io:format(user, "~p ~p: Received: ~p~n", [self(), now(), Message]),
            listener();
        Any ->
            io:format(user, "~p HURR! Unexpected message: ~p~n", [self(), Any]),
            listener()
    end.

bcast(Pids, Message) ->
    [P ! {bcast, Message} || P <- Pids],
    ok.

您在代码中遇到的其他问题不是真正的广播问题,而是以不熟悉的语言超越自己的问题。

这个例子是异步的(我们只是以一种方式发送消息)并且要求我们杀死我们的从属进程,因为我只编写了一个无限循环。这些方面是第一件要做的事情:如何处理消息队列(如在整个邮箱中)不仅仅是发送消息,还在循环中接收消息;并且考虑当事情进展顺利时你希望你的下属进程如何死亡(我只是在上面的例子中将它们全部谋杀)。