Concurrency 在Erlang中,多个进程如何同时使用一个公共列表?
我知道Erlang是关于并发的,我们使用spawn/spawn_link来创建一个进程,但我不明白的是,所有进程如何能够同时使用一个通用的用户列表?比如说ordict/dict存储 我想做的是; 1.派生的用户进程订阅/侦听已注册的进程 2.注册进程A存储所有联机用户的{Pid,Userid} 3.当某个用户发送消息时,用户的进程会询问该进程收件人是否在线Concurrency 在Erlang中,多个进程如何同时使用一个公共列表?,concurrency,erlang,Concurrency,Erlang,我知道Erlang是关于并发的,我们使用spawn/spawn_link来创建一个进程,但我不明白的是,所有进程如何能够同时使用一个通用的用户列表?比如说ordict/dict存储 我想做的是; 1.派生的用户进程订阅/侦听已注册的进程 2.注册进程A存储所有联机用户的{Pid,Userid} 3.当某个用户发送消息时,用户的进程会询问该进程收件人是否在线 在erlang中发送消息是异步的,但是当一个用户被多个用户发送消息时,它也是异步的吗?您可以将进程设置为a,并将存储在线用户的任何数据结构保
在erlang中发送消息是异步的,但是当一个用户被多个用户发送消息时,它也是异步的吗?您可以将进程设置为a,并将存储在线用户的任何数据结构保持为进程状态。存储新用户或删除一个用户可以用来完成,检查用户是否在线可以用来完成。或者,您可以让gen_服务器创建一个公开可读的,允许任何进程读取它以检查在线用户,但是存储和删除仍然需要对gen_服务器进行强制转换。您甚至可以使表公开可读写,以便任何进程都可以存储、删除或检查用户。但请记住,创建ets表的进程死亡时,ets表在默认情况下会被销毁,因此,即使创建它的gen_服务器死亡,您也需要保留它,您必须安排它被其他进程继承,或者。您可以将进程设为A,并保留任何存储在线用户的数据结构作为进程状态。存储新用户或删除一个用户可以用来完成,检查用户是否在线可以用来完成。或者,您可以让gen_服务器创建一个公开可读的,允许任何进程读取它以检查在线用户,但是存储和删除仍然需要对gen_服务器进行强制转换。您甚至可以使表公开可读写,以便任何进程都可以存储、删除或检查用户。但请记住,创建ets表的进程终止时,ets表默认会被销毁,因此,如果即使创建ets表的gen_服务器终止,您也需要保留ets表,则必须安排它被其他进程继承,或者。一个严肃的解决方案应该使用OTP行为gen_服务器、supervisor。。。正如史蒂夫建议的那样。 无论如何,我编写了一个小的示例模块,它实现了服务器和客户端,可以使用命令erl-sname test在一个节点上启动,例如,也可以使用命令erl-sname node1、erl-sname node2在多个节点上启动 它还包括一个shell会话示例,演示了大多数情况,我希望它可以帮助您跟踪进程之间的同步或异步交换 注意:对用户列表的访问不是并发的,如果该列表像本例中那样由服务器进程拥有,则不可能。这就是为什么Steve建议使用ETS来存储信息并进行真正的并发访问。我曾尝试使用接口编写示例,这些接口应该允许使用ETS而不是元组列表进行快速重构 以下是事件查看器的摘录:
一个严肃的解决方案应该使用OTP行为gen_服务器、主管。。。正如史蒂夫建议的那样。 无论如何,我编写了一个小的示例模块,它实现了服务器和客户端,可以使用命令erl-sname test在一个节点上启动,例如,也可以使用命令erl-sname node1、erl-sname node2在多个节点上启动 它还包括一个shell会话示例,演示了大多数情况,我希望它可以帮助您跟踪进程之间的同步或异步交换 注意:对用户列表的访问不是并发的,如果该列表像本例中那样由服务器进程拥有,则不可能。这就是为什么Steve建议使用ETS来存储信息并进行真正的并发访问。我曾尝试使用接口编写示例,这些接口应该允许使用ETS而不是元组列表进行快速重构 以下是事件查看器的摘录:
除了Steve answer,我还提供了一个玩具模块来说明它是如何工作的。除了Steve answer,我还提供了一个玩具模块来说明它是如何工作的。如何获取事件查看器图片?在send_trace函数中,我添加了一个对et:trace_me/5的调用,它只做了预配置,以便使用et_viewer:start进行跟踪[{trace_global,true},{trace_pattern,{et,max},{max_actors,20}]我在trace/0函数中调用。请注意,您需要wx_小部件在您的平台上工作。顺便说一句,如果有人知道如何将进程和服务器强制在同一时间线上,我将不胜感激。如何获取事件查看器图片?在send_trace函数中,我添加了对et:trace_me/5的调用,它除了预配置为使用et_查看器跟踪:我在trace/0函数中调用的start[{trace_global,true},{trace_pattern,{et,max},{max_actors,20}] 你需要wx_小部件在你的平台上工作。顺便说一下,如果有人知道如何将进程和服务器强制在同一时间线上,我将不胜感激。
-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").