2

私はアーラン初心者で、最初のメッセージングアプリケーションを構築しようとしています。私の質問は2つの部分に分かれています

-sname属性を指定せずにターミナルで2つの別々のerlangコンソールを開くと、両方のerlangコンソールが同じプロセスpidを持つ方法を教えてください。それらは実際には同じプロセスですか、どのように?


Terminal #1 
--------------

$> erl 

Erlang R15B01 (erts-5.9.1) [source] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.9.1  (abort with ^G)
1> self().
0.31.0

ターミナル2についても同様です


Erlang R15B01 (erts-5.9.1) [source] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9.1  (abort with ^G)
1> self().
0.31.0

両方を同じプロセスで実行するにはどうすればよいですか。これらのerlangプロセスはネイティブプロセスではありませんか?

メッセージングアプリケーションを構築していて、各ユーザーによって送信されているメッセージの情報を追跡する方法が必要な場合。私が使用しようとした基本的な設計は、ユーザーごとに新しいチャットクライアントを作成することです。メッセージを受信した人を追跡し、そこにニックネームを保存するプロセスを作成します。

同様に、誰がメッセージを送信したかを追跡するために使用される別のモジュールを作成しましたが、この場合、self()Pidを使用して送信者を追跡しました。これはモジュールで行われchat_client_senderます。


-module(chat_client).
-export([start/0, stop/0, loop/1, login/3, logout/1, send_message/2]).
-define(SERVER, chat_client).

start() ->
    message_router:start(),
    chat_client_sender:start().

stop() ->
    message_router:stop(),
    chat_client_sender:stop().

login(Uid, Password, Nickname) ->
    io:format("~p My Pid", [self()]),
    Pid = spawn(chat_client, loop, [Nickname]),     
    case message_router:login(Uid, Password, Nickname, Pid) of
        {ok, logged_in} ->
            chat_client_sender:add_sender(self(), {Uid, Nickname}),
            {ok, logged_in};
        {error, invalid_uid_or_pwd} ->
            {error, invalid}
    end.

logout(Uid) ->
    case message_router:logout(Uid) of
        {ok, logged_out} ->
            {ok, logged_out};       
        ignored ->
            ignored;
        _Someshit ->
            io:format("Some Shit ~p", _Someshit)
    end.

send_message(ToUid, MessageBody) ->
    chat_client_sender:send_message(ToUid, MessageBody).


loop(Nickname) ->
    receive
        {print_msg, Messagebody, SenderNickname} ->
            io:format("~p: ~p to ~p~n", [SenderNickname, Messagebody, Nickname]),
            loop(Nickname);     
        stop -> 
            ok
    end.    


chat_client_senderモジュール


-module(chat_client_sender).
-export([start/0, stop/0, add_sender/2, loop/1, get_sender/1, send_message/2]).
-define(SERVER, chat_client_sender).


start() ->
    erlang:register(?SERVER, spawn(chat_client_sender, loop, [dict:new()])).

stop() ->
    ?SERVER ! stop.

add_sender(SenderPid, {Uid, Nickname}) ->
    io:format("Adding Sender ~p ~p ~p ~n", [SenderPid, Uid, Nickname]),
    ?SERVER ! {add_sender, SenderPid, {Uid, Nickname}}.

get_sender(SenderPid) ->
    ?SERVER ! {get_sender, SenderPid}.

send_message(ToUid, MessageBody) ->
    ?SERVER ! {send_msg, ToUid, MessageBody}.

loop(MemberPids) ->
    receive
        {add_sender, SenderPid, {Uid, Nickname}} ->
            case dict:find(SenderPid, MemberPids) of
                {ok, {Uid, Nickname}} ->    
                    io:format("Pid exists ~n"),             
                    loop(MemberPids);
                error ->
                    loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
            end;
        {send_msg, ToUid, MessageBody, SenderPid} ->
            case get_sender(SenderPid, MemberPids) of
                {found, _Uid, Nickname} ->
                    message_router:send_message(ToUid, MessageBody, Nickname),
                    loop(MemberPids);
                not_found ->
                    not_found,
                    loop(MemberPids)
            end;
        {remove_sender, SenderPid} ->
            case get_sender(SenderPid, MemberPids) of
                {found, _Uid, _Nickname} ->
                    loop(dict:erase(SenderPid, MemberPids));
                not_found ->
                    ignored,
                    loop(MemberPids)
            end;
        {get_sender, SenderPid} ->
            case get_sender(SenderPid, MemberPids) of
                Any -> 
                    io:format("GET SENDER ~p~n", [Any])
            end,
            loop(MemberPids);
        stop ->
            ok
    end.


get_sender(SenderPid, MemberPids) ->
    case dict:find(SenderPid, MemberPids) of 
        {ok, {Uid, Nickname}} ->
            {found, {Uid, Nickname}};
        error ->
            not_found
    end.


そのため、アプリケーションは、から到着するSenderPidを格納するメソッドのatadd_sender句で失敗し始めます。loopchat_client

これが例です


 chat_client:start().
true
9> chat_client_sender:add_sender(self(), {'sid@abc.com', 'sid'}).
Adding Sender  'sid@abc.com' sid 
{add_sender,,{'sid@abc.com',sid}}
10> chat_client_sender:add_sender(self(), {'sid1@abc.com', 'sid1'}).
Adding Sender  'sid1@abc.com' sid1 
{add_sender,,{'sid1@abc.com',sid1}}
11> 
=ERROR REPORT==== 13-Oct-2012::19:12:42 ===
Error in process  with exit value: {{case_clause,{ok,{'sid@abc.com',sid}}},[{chat_client_sender,loop,1,[{file,"chat_client_sender.erl"},{line,25}]}]} 

私の理解によると、モジュール{ok, {Uid, Nickname}}内の句の末尾再帰を続行する必要があります。chat_client_sender


...
    receive
        {add_sender, SenderPid, {Uid, Nickname}} ->
            case dict:find(SenderPid, MemberPids) of
                {ok, {Uid, Nickname}} ->    
                    io:format("Pid exists ~n"),             
                    loop(MemberPids);
                error ->
                    loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
            end;
...

ここで何が起こっているのかを理解するのに助けていただければ幸いです。また、私のコードを確認して、ベストプラクティスや私がもっとうまくできることについてもっと教えていただければ幸いです。私のコードはgoo.gl/yY4kRで入手できます

ありがとうございました

4

2 に答える 2

6

最初の質問:

PIDの最初の部分は、プロセスが0で実行されるerlangノードを示します。これは、プロセスがローカルノードで実行されていることを意味します。さて、あなたが得たPIDは、単なるerlangプロセスであるシェルのPIDです。たとえば、死ぬ(そして自動的にリスポーンする)ことができます:

1> self().
<0.32.0>
2> [X] = [4,2].
** exception error: no match of right hand side value [4,2]
3> self().     
<0.35.0>

2番目の質問:

エラーはここにあります(問題のあるコードの最小限の部分を見つけることは、自分でデバッグを行っている場合だけでなく、質問を投稿する場合にも非常に役立ちます-通常、必要なコードのときに多くの人が助けようとします研究は小規模です。また、私が知る限り、SOは実際にはコードレビューに焦点を当てていません):

loop(MemberPids) ->
    receive
        {add_sender, SenderPid, {Uid, Nickname}} ->
            case dict:find(SenderPid, MemberPids) of
                {ok, {Uid, Nickname}} ->    
                    io:format("Pid exists ~n"),             
                    loop(MemberPids);
                error ->
                    loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
            end
    end.

したがって、次loop/1の形式の1種類のメッセージを{add_sender, SPID, {UID, Nick}}
受信します。このようなメッセージを受信すると、辞書で値を持つレコードを検索します。辞書は、形式の2つまたは複数の形式でSPID 応答します。それらを分離するために使用します。 ただし、一致させようとする場合は、新しい変数を使用しないでください。すでにインスタンス化されている古い変数を使用するため、それらが異なる場合は失敗します(大文字と小文字の句が一致しないため)。 error{ok, {UID2, Nick2}}case
{ok, {UID2, Nick2}}{UID, Nick}

それで、あなたは何ができますか?とにかくそれらを使用しないので、アンダースコアに置き換えることができます。

loop(MemberPids) ->
    receive
        {add_sender, SenderPid, {Uid, Nickname}} ->
            case dict:find(SenderPid, MemberPids) of
                {ok, {_, _}} ->    
                    io:format("Pid exists ~n"),             
                    loop(MemberPids);
                error ->
                    loop(dict:store(SenderPid, {Uid, Nickname}, MemberPids))
            end
    end.

ところで、使用しUidたり、個別に使用したりすることはないので、タプルを変数Nicknameに置き換えることができます。{Uid,Nickname}

loop(MemberPids) ->
    receive
        {add_sender, SenderPid, Value} ->
            case dict:find(SenderPid, MemberPids) of
                {ok, _} ->    
                    io:format("Pid exists ~n"),             
                    loop(MemberPids);
                error ->
                    loop(dict:store(SenderPid, Value, MemberPids))
            end
    end.

辞書が次のような値を返すと最初のバージョンが失敗するため、完全に同等ではありませんが{ok, {a,b,c}}、このチェックのポイントは実際にはわかりません。

dict:is_key/2さらに、新しい要素がすでに存在するかどうかを確認したいだけなので、(少なくとも意味的には)使用する方が適切です。また、実装によっては、キーが存在するかどうかを確認するだけで、値を取得する必要がないため、より高速になる場合があります。ただし、私はそれをテストしていないので、おそらくそのような最適化はありません。その上、それはおそらく小規模プロジェクトにとっては何の違いもありません。

ただし、特別な場合があることに注意してください。同じPIDと同じ値を持つユーザーを追加しようとするとどうなりますか(基本的に同じユーザーを再登録します)。現在の実装でも同じエラーが発生します。仕様によっては、それを変更したい場合があります。

楽しむ!

于 2012-10-13T17:27:46.577 に答える
4

最初の質問への回答:erlangプロセスはネイティブプロセスではなく、「内部」プロセスです。したがって、2つのerlangノードを実行する場合、それぞれに独自のプロセスのセットがあります。どちらの場合も、呼び出すシェルプロセスself()は同じpid(プロセス識別子)を持っています。

誰がメッセージを送信したかを決定する暗黙的または組み込みの方法はありません。標準的な方法は、例のように実行し、メッセージに送信者を明示的に含めることです。これが最善の方法であることがわかりました。

于 2012-10-13T18:38:19.603 に答える