2016-07-24 3 views
1

Ich verstehe, dass Erlang dreht sich alles um Nebenläufigkeit und wir verwenden spawn/spawn_link, um einen Prozess zu erstellen, was ich nicht verstehe, ist Wie können alle Prozesse eine gemeinsame Liste von Benutzern gleichzeitig verwenden? sagen Sie einen ordict/dict Speicher.Wie können mehrere Prozesse in Erlang gleichzeitig eine gemeinsame Liste verwenden?

Was ich versuche zu tun ist;
1. Spawned Benutzerprozess Abonniert/Hört registrierte Prozess A

2. Registrierte Prozess A speichert {Pid, Userid} aller Online-Nutzer

3. Wenn einige Benutzer einen Prozess Prozess A ob Empfänger Online fragt die Nachricht von Benutzer sendet oder nicht.

Senden einer Nachricht in erlang ist asynchron, aber ist es auch asynchron, wenn ein Benutzer Nachrichten von mehreren Benutzern gesendet wird?

+0

neben Steve Antwort, ich bot ein „Spielzeug“ Modul zu zeigen, wie es funktioniert. – Pascal

Antwort

4

Sie können den Prozess A eine gen_server process machen und jede Datenstruktur, die Online-Benutzer speichert, als Prozessstatus behalten. Das Speichern eines neuen Benutzers oder das Löschen eines neuen Benutzers könnte mit gen_server:cast/2 erfolgen und die Überprüfung, ob ein Benutzer online ist, könnte mit gen_server:call/2 erledigt werden. Alternativ können Sie eine gen_server erstellen eine öffentlich lesbare ets table, um jedem Prozess zu ermöglichen, es zu lesen, um für Online-Benutzer zu überprüfen, aber speichern und löschen würde immer noch Umwandlungen an die gen_server erfordern. Sie können die Tabelle sogar öffentlich lesbar und beschreibbar machen, so dass jeder Prozess Benutzer speichern, löschen oder überprüfen kann. Aber bedenken Sie, dass eine ets Tabelle standardmäßig zerstört wird, wenn der Prozess, der sie erstellt, stirbt. Wenn Sie sie also benötigen, auch wenn die gen_server, die sie erstellt hat, stirbt, müssen Sie dafür sorgen, dass sie von einem anderen Prozess geerbt wird. oder give it to a supervisor.

1

Eine ernsthafte Lösung sollte die OTP-Verhaltensweisen (gen_server, supervisor ...) wie von Steve vorgeschlagen verwenden. Jedenfalls habe ich ein kleines Beispielmodul geschrieben, das sowohl einen Server als auch Clients implementiert und das auf einem Knoten beispielsweise mit dem Befehl erl -sname test gestartet werden kann (oder mehrere Knoten mit erl -sname node1, erl -sname node2 ...).

es enthält auch ein Beispiel für eine Shell-Sitzung, die die meisten Fälle veranschaulicht, ich hoffe, es kann Ihnen helfen, den Austausch zu verfolgen, synchron oder asynchron zwischen Prozessen.

HINWEIS:der Zugriff auf die Benutzerliste ist nicht gleichzeitig, es ist nicht möglich, wenn die Liste von einem Serverprozess gehört, wie es in diesem Beispiel ist. Aus diesem Grund schlägt Steve vor, eine ETS zu verwenden, um die Informationen zu speichern und echte gleichzeitige Zugriffe auszuführen. Ich habe versucht, das Beispiel mit Schnittstellen zu schreiben, die ein schnelles Refactoring mit ETS statt Tupel-Liste erlauben sollten.

-module(example). 

-export([server/0,server_stop/1,server_register_name/2,server_get_address/2, server_quit/2, % server process and its interfaces 
     client/1,quit/1,register_name/2,get_address/2,send_message/3,print_messages/1, % client process and its interfaces 
     trace/0]). % to call the tracer for a nice message view 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
% Client interface 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

client(Node) -> 
    % connect the current node to the servernode given in parameter 
    % it will fail if the connection cannot be established 
    true = net_kernel:connect_node(Node), 
    % spawn a client process 
    spawn(fun() -> client([],unregistered,{server,Node}) end). 

register_name(ClientPid,Name) -> 
    % use a helper to facilitate the trace of everything 
    send_trace(ClientPid,{register_name,self(),Name}), 
    % wait for an answer, it is then a synchronous call 
    receive 
     % no work needed, simply return any value 
     M -> M 
    after 1000 -> 
     % this introduce a timeout, if no answer is received after 1 second, consider it has failed 
     no_answer_from_client 
    end. 

get_address(ClientPid,UserName) -> 
    send_trace(ClientPid,{get_address,self(),UserName}), 
    % wait for an answer, it is then a synchronous call 
    receive 
     % in this case, if the answer is tagged with ok, extract the Value (will be a Pid) 
     {ok,Value} -> Value; 
     M -> M 
    after 1000 -> 
     no_answer_from_client 
    end. 

send_message(ClientPid,To,Message) -> 
    % simply send the message, it is asynchronous 
    send_trace(ClientPid,{send_message,To,Message}). 

print_messages(ClientPid) -> 
    send_trace(ClientPid,print_messages). 

quit(ClientPid) -> 
    send_trace(ClientPid,quit). 



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
% client local functions 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

client(Messages,Name,Server) -> 
    receive 
     {register_name,From,UserName} when Name == unregistered -> 
      % if not yet registered send the request to the server and 
      % backward the answer to the requester 
      Answer = server_register_name(Server,UserName), 
      send_trace(From,Answer), 
      NName = case Answer of 
       registered -> UserName; 
       _ -> Name 
      end, 
      client(Messages,NName,Server); 
     {register_name,From,_} -> 
      % if already registered reject the request 
      send_trace(From,{already_registered_as,Name}), 
      client(Messages,Name,Server); 
     {get_address,From,UserName} when Name =/= unregistered -> 
      Answer = server_get_address(Server,UserName), 
      send_trace(From,Answer), 
      client(Messages,Name,Server);   
     {send_message,To,Message} -> 
      % directly send the message to the user, the server is not concerned 
      send_trace(To,{new_message,{erlang:date(),erlang:time(),Name,Message}}), 
      client(Messages,Name,Server); 
     print_messages -> 
      % print all mesages and empty the queue 
      do_print_messages(Messages), 
      client([],Name,Server); 
     quit -> 
      server_quit(Server,Name); 
     {new_message,M} -> 
      % append the new message 
      client([M|Messages],Name,Server); 
     _ -> 
      client(Messages,Name,Server) 
     end. 

do_print_messages(Messages) -> 
    lists:foreach(fun({D,T,W,M}) -> io:format("from ~p, at ~p on ~p, received ~p~n",[W,T,D,M]) end,Messages). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
% Server interface 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

server() -> 
    true = register(server,spawn(fun() -> server([]) end)), 
    node(). 

server_stop(Server) -> 
    send_trace(Server,stop). 

server_register_name(Server,User) -> 
    send_trace(Server,{register_name,self(),User}), 
    receive 
     M -> M 
    after 900 -> 
     no_answer_from_server 
    end. 

server_get_address(Server,User) -> 
    send_trace(Server,{get_address,self(),User}), 
    receive 
     M -> M 
    after 900 -> 
     no_answer_from_server 
    end. 

server_quit(Server,Name) -> 
    send_trace(Server,{quit,Name}). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
% server local functions 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

server(Users) -> 
    receive 
     stop -> 
      ok; 
     {register_name,From,User} -> 
      case lists:keyfind(User,1,Users) of 
       false -> 
        send_trace(From,registered), 
        server([{User,From}|Users]); 
       _ -> 
        send_trace(From,{already_exist,User}), 
        server(Users) 
       end; 
     {get_address,From,User} -> 
      case lists:keyfind(User,1,Users) of 
       false -> 
        send_trace(From,{does_not_exist,User}), 
        server(Users); 
       {User,Pid} -> 
        send_trace(From,{ok,Pid}), 
        server(Users) 
       end; 
     {quit,Name} -> 
      server(lists:keydelete(Name,1,Users)) 
    end. 



%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
% global 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

trace() -> 
% start a collector, a viewer and trace the "trace_me" ... 
    et_viewer:start([{trace_global, true}, {trace_pattern, {et,max}},{max_actors,20}]). 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
% helpers 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

send_trace(To,Message) -> 
    % all messages will be traced by "et" 
    et:trace_me(50,self(),To,Message,[]), 
    To ! Message. 

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
% shell commands 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 

% c(example). 
% example:trace(). 
% N = node(). 
% C1 = example:client(N). 
% example:register_name(pid(0,5555,0),"fails"). 
% example:register_name(C1,"fails_again"). 
% example:server(). 
% example:register_name(C1,"Joe"). 
% C2 = example:client(N). 
% example:register_name(C2,"Bob"). 
% example:print_messages(C1). 
% C2 = example:get_address(C1,"Bob"). 
% example:send_message(C1,C2,"Hi Bob!"). 
% example:send_message(C1,C2,"Hi Bob! are you there?"). 
% example:print_messages(C2). 
% example:send_message(C2,C1,"Hi Joe! Got your message."). 
% example:print_messages(C2). 
% example:print_messages(C1). 
% example:quit(C1). 
% example:get_address(C2,"Joe"). 
% example:server_stop({server,N}). 
% example:get_address(C2,"Joe"). 
% example:get_address(C1,"Bob"). 

hier ein Auszug aus der Ereignisanzeige:

enter image description here

+0

Wie bekomme ich das "Event Viewer" -Bild? – BlackMamba

+0

In der send_trace-Funktion habe ich einen Aufruf von et: trace_me/5 hinzugefügt, es tut nichts, aber es ist "vorkonfiguriert", verfolgt zu werden mit etviewer: start ([trace_global, true}, {trace_pattern, {et, max}}, {max_actors, 20}]) 'das rufe ich in der Funktion trace/0 auf. Beachten Sie, dass Sie wx_widget auf Ihrer Plattform verwenden müssen.By the way, wenn jemand weiß, wie man den Prozess <0,59,0> und Server auf der gleichen Zeitlinie erzwingen, würde ich mich freuen. – Pascal

Verwandte Themen