web-dev-qa-db-ja.com

非同期コンテンツを含むブロッキング関数呼び出し

これは一般的なデザインパターンだと思いますが、盲点があるようです。

enter image description here

アプリケーションからサービスへの関数呼び出しをブロックする必要がありますが、サービスは非同期で何かを行う必要があります。

まあ、私はそれを扱うことができます[〜#〜] but [〜#〜]、複数のアプリケーションがあり、サービスは1つだけです。

サービスはアプリケーションをブロックする必要がありますが、異なるアプリケーションからの複数の並列リクエストを処理できる必要もあります。

アプリケーションのリクエストごとに新しいスレッドを生成する必要があると思いますが、最後にどのようにしてスレッドからアプリケーションに戻ることができますか?

アプリケーションの戻りアドレスを格納して後でポップするアセンブラー挿入は問題ありませんが、それは本当に必要ですか?

サービス関数を再入可能としてマークするには、C++で特別なことをする必要がありますか?

誰かが私の目隠しを持ち上げてくれますか?


【更新】アプリケーション間に相関関係はありません。また、アプリケーションではなく、サービスのみがスレッドの生成を許可されます。したがって、service mainservice threadsの束を生成することを考えていますが、関数呼び出しのブロックと戻りをどのように処理できるかわかりません。

リクエストごとにブロッキング構造を使用する必要があります。

これは Futures(programming) と呼ばれます。

各リクエストには独自のFutureがあります。リクエスト処理が開始されると(おそらく、別のスレッドプールで、説明したように)、呼び出し元はFutureのフルフィルメントまたは失敗時にブロックされます。

結果が到着すると、Futureのブロックが解除され、呼び出しはアプリケーションに戻ります。

このパターンを使用するときは、デッドロックとゾンビを防ぐために細心の注意を払う必要があります。つまり、結果が成功したか失敗したかに関係なく、Futureのブロックを解除する必要があります(失敗した場合、アプリケーションに例外をスローするのは公正なゲームです)。

スレッドプールの設計に関して:

  • 同時リクエスター(つまりアプリケーション)ごとに少なくとも1つのスレッドが存在し、要求中にブロックされます。
    • アプリケーションとサービスが同じプロセス内にある場合、これらのスレッドはアプリケーションスレッドと同じであり、ブロックされます。
  • 個別のスレッドプールがあり、サービスとインターネットの間で作業を行うために必要な数のスレッドが必要です。これらのスレッドは、リクエストを受け入れるスレッドに追加されます。
    • 正確な数は、作業の方法によって異なります。ここでは非同期パターン(reactor)を使用できます。これにより、必要なスレッドの数を減らすことができますが、要求を受け入れるスレッドの数には影響しません。

アプリケーションからサービスへのリクエストがネットワークを介して発生した場合、アプリケーションスレッドとサービススレッドは分離されます(つまり、アプリケーションリクエストのブロックには「スレッド」はまったく含まれず、ネットワークからの無応答のみです。その場合、Serviceはreactorパターンを使用することもできます。つまり、スレッドの数をさらに減らすことができます。

4
rwong

アプリケーションはサービスコール中はブロックされるため、同時に複数のサービスコールが発生する場合は、すべてのアプリケーションに独自のスレッドが必要です。

サービス呼び出しが非同期操作を単に開始し、おそらく何か他のことを行い、操作が完了するまでブロックすると仮定すると、問題は発生しません。サービスの呼び出しはすべて独自のスレッド上で行われるため、そのスレッドをブロックするだけで済みます。

1
Sebastian Redl

あなたの質問に対する短い答えは、非同期コードから戻らないということです。代わりに、コンテキストオブジェクトを非同期コードに渡して、要求の処理が完了したときに応答を返します。これは主に2つの形式で行われます。

あなたはすでに非同期を処理するいくつかのサーバーフレームワークを使用しています:

void init() {
    registerHandler("requestType", [](ReqType req, Response resp) {
        // ..
        resp.send(data);
    });
}

または、ブロッキングイベントループでクライアント要求を処理する場合は、次のようにマルチスレッドを使用して非同期を実現できます。

while (true) {
    // ...
    Req req = recv();
    thread t(processReq, req, ctx);
}

void processReq(Req req, Context &ctx) {
    // ...
    ctx.send(response);
}

1つの要求を処理するために複数の非同期操作を実行する必要がある場合、非同期操作がfuture/promiseタイプを返す場合、futureを使用して非同期操作をチェーンします。これは、非同期操作が戻るのを待つことと同じではありません。

void processReq(Req req, Context &ctx) {
    // ...
    op1().then(op2).then(op3);
}

最後に、非同期コードが終了してからプロセスを終了するのを待つ場合は、スレッドオブジェクトのjoinメソッドを使用するか、promiseオブジェクトのwaitメソッドを使用します。

1
Zhengyang