5

メッセージ駆動型アプリを作成するとき。内部操作にメッセージングを広範囲に使用するという点だけが標準のWindowsアプリと同じように、スレッド化に関して最善のアプローチは何でしょうか。

私が見ているように、基本的に3つのアプローチがあります(他のセットアップを念頭に置いている場合は、共有してください)。

  1. シングルスレッドですべてのメッセージを処理します。
  2. 個別のメッセージタイプ(一般、UI、ネットワークなど)に個別のスレッドを使用する
  3. 単一のメッセージキューを共有および処理する複数のスレッドを持つ。

では、3つの間に大きなパフォーマンスの違いはありますか?一般的な考え方は次のとおりです。明らかに、最後の2つのオプションは、複数のプロセッサが存在する状況から恩恵を受けます。さらに、いずれかのスレッドが外部イベントを待機している場合でも、他のスレッドは無関係のメッセージを処理できます。しかし、それを無視すると、複数のスレッドはオーバーヘッドを追加するだけのようです(より複雑な同期状況は言うまでもなく、スレッドスイッチ)。

そして別の質問:このようなシステムを標準のWindowsメッセージングシステムに実装することをお勧めしますか、それとも別のキューメカニズムを実装することをお勧めしますか?その理由は何ですか?

4

10 に答える 10

8

スレッド モデルの特定の選択は、解決しようとしている問題の性質によって決定される必要があります。このようなアプリケーションのスレッド モデルを設計するための "正しい" アプローチは、必ずしも 1 つではありません。ただし、次の仮定を採用する場合:

  1. メッセージが頻繁に届く
  2. メッセージは独立しており、共有リソースに過度に依存していません
  3. 到着したメッセージにできるだけ早く応答することが望ましい
  4. 処理アーキテクチャ (つまり、マルチコード/マルチ CPU システム) 全体でアプリを適切にスケーリングする必要がある
  5. スケーラビリティが重要な設計要件です (例: より多くのメッセージをより高速に)
  6. スレッド障害に対する回復力 / 長い操作が望ましい

私の経験では、最も効果的なスレッド化アーキテクチャは、スレッド プールを使用することです。すべてのメッセージが 1 つのキューに到着し、複数のスレッドがキューで待機し、到着したメッセージを処理します。スレッド プールの実装は、スレッド分散の 3 つの例すべてをモデル化できます。

#1 シングル スレッドがすべてのメッセージを処理 => スレッド プールが 1 つだけのスレッド

#2 N メッセージ タイプあたりのスレッド => N スレッドのスレッド プール。各スレッドはキューを覗いて適切なメッセージ タイプを見つけます。

#3 すべてのメッセージに複数のスレッド => 複数のスレッドを持つスレッド プール

この設計の利点は、処理環境またはメッセージの負荷に比例してスレッド内のスレッド数をスケーリングできることです。スレッドの数は、発生しているリアルタイムのメッセージ負荷に適応するために、実行時にスケーリングすることもできます。

.NET、C++/STL、Java など、ほとんどのプラットフォームで使用できる優れたスレッド プーリング ライブラリが多数あります。

2 番目の質問については、標準の Windows メッセージ ディスパッチ メカニズムを使用するかどうかです。このメカニズムにはかなりのオーバーヘッドが伴い、実際には、Windows アプリケーションの UI ループを介してメッセージをポンピングすることだけを目的としています。これが解決しようとしている問題でない限り、一般的なメッセージ ディスパッチ ソリューションとして使用しないことをお勧めします。さらに、Windows メッセージが運ぶデータは非常に少なく、オブジェクト ベースのモデルではありません。各 Windows メッセージには、コードと 32 ビットのパラメーターがあります。これは、明確なメッセージング モデルの基礎として十分ではない可能性があります。最後に、Windows メッセージ キューは、キューの飽和、スレッドの枯渇、またはメッセージの再キューイングなどのケースを処理するように設計されていません。これらは、適切なメッセージ キューイング ソリューションを実装する際によく発生するケースです。

于 2009-04-21T15:04:31.640 に答える
3

ワークロード (つまり、時間の経過に伴うイベントの統計的分布) を知らなければ、多くのことを確実に伝えることはできませんが、一般的には

  • 複数のサーバーを持つ単一のキューは、少なくとも同じくらい速く、通常はより高速であるため、2 よりも 1,3 の方が望ましいでしょう。
  • ほとんどの言語で複数のスレッドを使用すると、競合や複数ライターの問題を回避する必要があるため、複雑さが増します
  • 長時間のプロセスは、より迅速に完了できる他の処理をブロックする可能性があります。

したがって、複数のサーバー スレッドがキューからイベントを取得する単一のイベント キューを持つ方が、少し高速になる可能性があると馬に乗って推測できます。

キューにスレッドセーフなデータ構造を使用していることを確認してください。

于 2009-04-21T14:31:09.710 に答える
3

それはすべて依存します。

例えば:

  • イベントには暗黙の順序があるため、GUI キュー内のイベントは単一のスレッドで実行するのが最適です。したがって、イベントは連続して実行する必要があります。これが、ほとんどの GUI アプリがイベントを処理するための単一のスレッドを持っている理由ですが、イベントを作成するために複数のイベントが発生する可能性があります (また、イベント スレッドがジョブを作成し、それをワーカー プールに処理することを妨げるものではありません (以下を参照))。

  • 各リクエストはステートレスであり、したがって独立して実行できるため、ソケット上のイベントは潜在的に並行して実行できます (HTTP を想定)。

  • 作業ジョブは、各ジョブが独立しており、キューに配置されていました。これは、一連のワーカー スレッドを使用する典型的なケースです。各スレッドは、他のスレッドとは独立して、潜在的に長い操作を実行します。完了すると、別のジョブのキューに戻ります。

于 2009-04-21T14:57:31.707 に答える
1

一般に、スレッドのオーバーヘッドについて心配する必要はありません。それらのほんの一握りについて話しているのであれば、それは問題になりません。競合状態、デッドロック、および競合はより大きな懸念事項です。私が何について話しているのかわからない場合は、これに取り組む前に読むべきことがたくさんあります。

私は、選択した言語が提供する抽象化を使用して、オプション 3 を使用します。

于 2009-04-21T14:34:53.757 に答える
1

2 つの異なるパフォーマンス目標があることに注意してください。スループットと応答性のどちらを目標としているかは述べていません。

GUI アプリを作成している場合、UI はレスポンシブである必要があります。1 秒あたりに処理できるクリック数は気にしませんが、10 分の 1 秒程度 (理想的にはそれ以下) 以内に応答を表示することには注意を払います。これは、GUI の処理専用のスレッドを 1 つ持つことが最善である理由の 1 つです (他の理由は他の回答で言及されています)。GUI スレッドは、基本的に Windows メッセージを作業項目に変換し、ワーカー キューに重い作業を処理させる必要があります。ワーカーが完了すると、GUI スレッドに通知され、表示が更新されて変更が反映されます。ウィンドウの描画などを行いますが、表示するデータをレンダリングしません。これにより、ほとんどのユーザーがパフォーマンスについて語る際に求めている、迅速な「機敏さ」がアプリにもたらされます。彼らはしません

もう 1 つのパフォーマンス特性はスループットです。これは、特定の時間内に処理できるジョブの数です。通常、このタイプのパフォーマンス チューニングは、サーバー タイプのアプリケーションまたはその他の負荷の高い処理でのみ必要です。これは、1 時間に提供できる Web ページの数、または DVD のレンダリングにかかる​​時間を測定します。この種のジョブでは、CPU ごとに 1 つのアクティブなスレッドが必要です。それより少ないと、アイドル クロック サイクルを浪費することになります。それ以上の場合、スレッドは CPU 時間をめぐって競合し、互いにつまずきます。対処しているトレードオフについては、この記事のDDJ 記事の 2 番目のグラフを参照してください。理想的なスレッド数は、ブロッキングやロックなどのために、使用可能な CPU の数よりも多いことに注意してください。キーはの数ですアクティブなスレッド。

于 2009-04-21T15:23:11.457 に答える
0

メッセージキューにサービスを提供するスレッドプールを用意し、プール内のスレッドの数を簡単に構成できるようにします (おそらく実行時でも)。次に、予想される負荷でテストします。

そうすれば、実際の相関関係を確認できます。最初の仮定が変わった場合は、アプローチを簡単に変更できます。

より洗練されたアプローチは、システムが独自のパフォーマンス特性を内省し、リソース、特にスレッドの使用を適応させることです。おそらくほとんどのカスタム アプリケーション コードではやり過ぎですが、それを行う製品があることは確かです。

Windowsイベントの質問については、おそらくアプリケーション固有の質問であり、一般的なケースでは正しい答えも間違った答えもないと思います。そうは言っても、私は通常、手元のタスクの特定の特性に合わせて調整できるので、独自のキューを実装します。場合によっては、Windows メッセージ キューを介してイベントをルーティングする必要があります。

于 2009-06-21T22:20:29.343 に答える
0

まず、複数のスレッドが必要な理由を自問することから始めてください。

この質問に対するよく考えられた回答は、次の質問「アプリケーションで複数のスレッドをどのように使用すればよいですか?」に対する最良の回答につながります。

そして、それはその後の質問でなければなりません。主な質問ではありません。最初の質問は、方法ではなく、理由でなければなりません。

于 2009-04-21T14:32:52.810 に答える
0

各スレッドが実行される時間に依存すると思います。各メッセージの処理にかかる時間は同じですか? または、たとえば、特定のメッセージに数秒かかります。メッセージ A が完了するまでに 10 秒かかることがわかっていた場合、実行時間の長いスレッドのキューを保持したい理由があるため、間違いなく新しいスレッドを使用します...

私の2セント。

于 2009-04-21T14:33:29.860 に答える
0

オプション 2 が最適だと思います。各スレッドに独立したタスクを実行させると、最良の結果が得られます。3 番目のアプローチでは、複数のスレッドがディスク読み取り、共通ソケットの読み取りなどの I/O 操作を実行している場合、さらに遅延が発生する可能性があります。

要求の処理に Windows メッセージング フレームワークを使用するかどうかは、各スレッドの作業負荷によって異なります。私は窓がいいえを制限していると思います。キューに入れることができるメッセージの最大数は 10000 です。ほとんどの場合、これは問題になりません。ただし、キューに入れるメッセージがたくさんある場合は、これを考慮する必要があります。

別のキューは、必要に応じて並べ替えることができるという意味で、より適切な制御を提供します (優先度に応じて異なる場合があります)。

于 2009-04-21T15:27:52.147 に答える
0

はい、選択肢によってパフォーマンスに違いがあります。

(1) メッセージ処理のボトルネックが発生します
。(3) 共有キューへのアクセスを同期する必要があるため、ロックの競合が発生します。

(2) は正しい方向に進み始めています...ただし、各メッセージ タイプのキューは少し極端です。アプリのモデルごとにキューを作成することから始めて、パフォーマンスを向上させるためにキューを追加することをお勧めします。

オプション 2 が気に入った場合は、SEDA アーキテクチャの実装に興味があるようです。何が起こっているのかを理解するには少し読む必要がありますが、アーキテクチャはあなたの考え方によく合っていると思います。

ところで、Yieldは優れた C++/Python ハイブリッド実装です。

于 2009-04-21T18:07:49.983 に答える