4

私は erlang が初めてで、Joe Armstrong による「Programming Erlang」からチュートリアルを開始しています。

8.6 の選択受信で言及されている「セーブ キュー」に戸惑っています。メッセージがまったく一致しない場合は、直接ドロップしないでください。後で処理するためにメールボックスに戻す目的は何ですか? これがデフォルトの動作である場合、これらのガベージ メッセージ (照合できないことを意味します) は、解放されずに蓄積されるため、パフォーマンスの低下につながる可能性があります。

この部分を誤解していないかどうか知りたいです。プロセス メールボックスの内容を調べて理解を深めようとしましたが、できませんでした。

助けてください。ポイントを証明するコードスニペットをいただければ幸いです。ありがとうございます。

4

3 に答える 3

10

この本のこの段落では、1つの受信ブロックによって行われることの詳細について説明します。1つのプロセスで複数の受信ブロックを順番に処理できることを忘れないでください。最初の受信ブロックのどのエントリとも一致しない1つのメッセージは、次の理由で保存キューに入れられます(効率化のため)。

  • 現在の受信ブロックのどのエントリとも一致しません。
  • 現在の受信ブロックは、1つの入力メッセージが1つのエントリと一致するか、タイムアウトが終了した場合にのみ終了することが保証されています。

1つの受信ブロックが完了すると、保存キューは元の受信順序でメールボックスに戻されます。これは、次の受信ブロックが「キューに保存された」メッセージの1つと一致するエントリを持つ可能性があるためです。

1つの使用法は、優先順位を管理することです。

loop() ->
   ...
   receive
      {high_prio,Msg} -> process_priority_message(Msg)
   after 0
      ok  % no priority message
   end,

   receive
      {low_prio,Msg} -> process_normal_message(Msg);
      Other -> process_unexpected_message(Other)
   end,
   ...
   loop()

このコードを使用すると、メッセージキュー内の最初の位置にない場合でも、メッセージ{high_prio、Msg}を処理できます。

そして、あなたは正しいです、特に終わりのないループで、予期しないメッセージがメールボックスに蓄積するリスクがあります、それはあなたが最後の行のようなものを非常に頻繁に見る理由です

その他->process_unexpected_message(Other)

メールボックスを空にします。

于 2013-02-07T10:07:58.447 に答える
3

並行システムを構築する場合、その時点で関心のあるメッセージのみに集中し、他のメッセージを無視できるため、非常に役立ちます。Erlangシステムは通常非決定論的であるため、何をいつ受け取るかはほとんどわかりません。自動保存キューがないと、メッセージを受信するすべての時点で、このプロセスに到達する可能性のあるすべてのメッセージを処理できる必要があります。それはすぐに状態とメッセージの組み合わせ爆発になります。

例として、単純なサーバーを取り上げます。その最上位には、サーバーが処理する要求を受信する受信ループがあります。その後、最初のリクエストを処理します。ほとんどの場合、この処理中に他のプロセスと通信し、メッセージを受信します。リクエストの処理中に、新しいリクエストメッセージがサーバーに到着する可能性があります。Erlangがメッセージを保存しなかった場合、メッセージを受信するサーバーコードのどこでもこれらのリクエストを処理する必要があります。これで、これらの新しい要求を無視して、それらを処理する必要があるトップループに残すことができます。

サーバーのどこかで処理する必要のあるすべてのメッセージを追跡することは、すぐに機能しなくなります。たとえば、gen_serverクライアントから送信された実際のリクエスト(メッセージ形式は指定されていません)、サーバーへの「システム」メッセージ(メッセージ形式は指定されていません)、サーバーコードにとって意味のある明確に定義されたメッセージの数に加えて、リクエストの処理に必要なメッセージ。

最終的には、独自のメッセージバッファを実装し、それを渡すことになります。

メッセージ保存キューがないと、処理中にメッセージを送受信する汎用モジュールを作成することは事実上不可能になります。たとえば、クライアントはgen_serversに機能します。彼らは、そのプロセスに到達する可能性があり、処理する必要があるすべてのメッセージについて知る必要があります。

はい、すべてのメッセージを保存することは問題になる可能性がありますが、それは通常、あなたがそれを知っているときに解決できるタイプの問題です。たとえば、サーバーのトップループでは、不明なメッセージを受信して​​破棄できることを合理的に確信できます。Agen_serverは、それらをどう処理するかを知っているトップレベルですべてのメッセージを受信します。一部はそれ自体(システムメッセージ)を処理し、その他は特定のサーバーコードを渡します。

また、@ Pascalが示したように、優先メッセージを簡単に処理できます。

于 2013-02-07T20:49:07.560 に答える
2

+1 rvirding、「並行システムを構築するときに非常に役立ちます」はまさにそれについてです。いくつかのサンプル コード (「喫煙者の問題」の解決策) を思い出しました。しばらく前にクランクアップしました。それを共有すると、ポイントを説明するのに役立つかもしれないと思いました:

-module(smokers).
-export([smokers/0]).

smokers() ->
    Rounds = 1000000,
    Agent = self(),
    lists:foreach(fun(Material) -> spawn(fun() -> startSmoker(Agent, Material) end) end, materials()),
    done = agent(Rounds),
    io:format("Done ~p rounds~n", [Rounds]).

agent(0) ->
    done;
agent(Rounds) ->
    offer(twoRandomMaterials(), Rounds).

offer(AvailableMaterials, Rounds) ->
    receive
        {take, Smoker, AvailableMaterials} ->
            Smoker ! smoke,
            receive
                doneSmoking ->
                    agent(Rounds - 1)
            end
    end.

startSmoker(Agent, Material) ->
    smoker(Agent, lists:delete(Material, materials())).

smoker(Agent, Missing) ->
    Agent ! {take, self(), Missing},
    receive
        smoke ->
            Agent ! doneSmoking,
            smoker(Agent, Missing)
    end.

twoRandomMaterials() ->
    Materials = materials(),
    deleteAt(random:uniform(length(Materials)) - 1, Materials).

materials() ->
    [paper, tobacco, match].

deleteAt(_, []) -> [];
deleteAt(0, [_ | T]) -> T;
deleteAt(Idx, [H | T]) -> [H | deleteAt(Idx - 1, T)].

ここで興味深いのは、receive {take, Smoker, AvailableMaterials}. ただし、現在処理できるのはそのうちの1 つだけです。それから握手としてのインナーがあります。したがって、ハンドシェイク メッセージの受信時にコードが何らかの作業を実行できるようにする適切なメッセージの選択と、ハンドシェイク メッセージの受信時に他のメッセージが失われないことが、ここでのすべての同時実行の問題を解決するものです。一致しない/処理不能なメッセージがいずれかの時点でドロップされた場合、は (定期的に要求を繰り返さない限り) 永久に停止します。receive doneSmokingtakesmoker

于 2013-02-08T02:04:49.687 に答える