2

erlangアプリケーションのコードを閲覧しているときに、興味深い設計上の問題に遭遇しました。状況を説明させていただきますが、PIAの関係でコードを投稿できません。

コードは、2つのgen_serverモジュールが何らかのリソースの割り当てを担当するOTPアプリケーションとして構造化されています。アプリケーションはしばらくの間完全に実行され、大きな問題は発生しませんでした。

トリッキーな部分は、最初の人がgen_server2番目の人に十分なリソースが残っているかどうかを確認する必要があるときに始まります。Acallは、(非常に特殊なケースで)call最初のgen_serverに発行するユーティリティライブラリを呼び出す2番目のgen_serverに発行されます。

私はerlangに比較的慣れていませんが、この状況では2つのgen_serverが互いに待機するようになると思います。

これはおそらく設計上の問題ですが、この種の「ハング」を防ぐことができる特別なメカニズムがOTPに組み込まれているかどうかを知りたかっただけです。

どんな助けでもいただければ幸いです。

編集:答えを要約するには:2つが周期的に相互に関係している状況でgen_servercall、アプリケーションの設計にもう少し時間を費やしたほうがよいでしょう。

ご協力いただきありがとうございます :)

4

3 に答える 3

4

これはデッドロックと呼ばれ、設計レベルで回避できる/回避する必要があります。以下は、考えられる回避策と、間違いを避けるのに役立つと思われるいくつかの主観的なポイントです。

問題を回避する方法はいくつかありますが、「待機」はまさにそれcallが行っていることです。

考えられる回避策の1つは、Bを呼び出すプロセスをAの内部から生成することですが、AがBからの呼び出しを処理するのをブロックしません。このプロセスは呼び出し元に直接応答します。

サーバーAの場合:

handle_call(do_spaghetti_call, From, State) ->
    spawn(fun() -> gen_server:reply(From, call_server_B(more_spaghetti)) end),
    {noreply, State};
handle_call(spaghetti_callback, _From, State) ->
    {reply, foobar, State}

サーバーBの場合:

handle_call(more_spaghetti, _From, State) ->
    {reply, gen_server:call(server_a, spaghetti_callback), State}

私にとって、これは非常に複雑で、推論するのが非常に困難です。誰も怒らせずにスパゲッティコードと呼べると思います。

別の注意点として、上記は問題を解決するかもしれませんが、このような呼び出しが実際に何を意味するのかをよく考える必要があります。たとえば、サーバーAがこの呼び出しを何度も実行するとどうなりますか?いずれかの時点でタイムアウトが発生した場合はどうなりますか?タイムアウトをどのように構成して、意味をなすようにしますか?(最も内側の呼び出しは、外側の呼び出しなどよりもタイムアウトを短くする必要があります)。

たとえそれが苦痛であっても、私はデザインを変更します。なぜなら、これを存在させて回避すると、システムが非常に推論しにくくなるからです。私見、複雑さはすべての悪の根源であり、どんな犠牲を払っても避けるべきです。

于 2011-05-17T21:19:35.220 に答える
3

これは主に、gen_server1からの呼び出しをブロックする時間が長くないことを確認する必要がある設計上の問題です。これは、gen_server2への呼び出しを処理し、完了時に結果をgen_server1に配信する小さなfunを生成することで、非常に簡単に実行できます。

gen_server1がgen_server2からの応答を待機しているという事実を追跡する必要があります。多分このような何か:

handle_call(Msg, From, S) ->
  Self = self(),
  spawn(fun() ->
    Res = gen_server:call(gen_server2, Msg),
    gen_server:cast(Self, {reply,Res})
  end),
{noreply, S#state{ from = From }}.

handle_cast({reply, Res}, S = #state{ from = From }) ->
  gen_server:reply(From, Res),
  {noreply, S#state{ from = undefiend}.

このようにして、gen_server1はハングすることなくgen_server2からの要求を処理できます。もちろん、小さなプロセスの適切なエラー伝播も行う必要がありますが、一般的な考え方は理解できます。

于 2011-05-17T21:18:53.417 に答える
1

それを行う別の方法は、私がより良いと思うのですが、この(リソース)情報を非同期で渡すようにすることです。my_resource_state各サーバーは、他のサーバーから(非同期の)メッセージを受け取ったときに反応し、想定どおりの動作をします。send_me_your_resource_stateまた、非同期メッセージでリソース状態を送信するように他のサーバーに促すこともできます。これらのメッセージは両方とも非同期であるため、ブロックされることはなく、サーバーはmy_resource_state、プロンプトを表示した後、他のサーバーからのメッセージを待機している間、他の要求を処理できます。

メッセージを非同期にすることのもう1つの利点は、サーバーが必要だと感じたときにプロンプ​​トが表示されることなく、この情報を送信できることです。または「私はあふれています、あなたはいくつか欲しいですか?」。

@Lukasと@knutinからの2つの応答は、実際には非同期で実行しますが、サーバーをブロックせずに同期呼び出しを実行できる一時的なプロセスを生成することによって実行します。非同期メッセージをすぐに使用する方が簡単で、意図も明確です。

于 2011-05-19T16:18:04.423 に答える