1

ソケットを発見して以来、スレッド化についてわざわざ学びたくなかったので、ノンブロッキングバリアントを使用してきました。それ以来、私は糸脱毛についてより多くの経験を積み、自分自身に問いかけ始めています。なぜあなたはそれをソケットに使うのでしょうか?

スレッド化の大前提は、独自のデータセットで作業できるようになった場合にのみ意味があるようです。同じデータセットで2つのスレッドを処理すると、次のような状況になります。

if(!hashmap.hasKey("bar"))
{
  dostuff               // <-- meanwhile another thread inserts "bar" into hashmap
  hashmap[bar] = "foo"; // <-- our premise that the key didn't exist
                        //     (likely to avoid overwriting something) is now invalid
}

ここで、リモートIPをパスワードにマップするハッシュマップを想像してみてください。あなたは私がどこに行くのか見ることができます。確かに、そのようなスレッドの相互作用がうまくいかない可能性はかなり低いですが、それはまだ存在しており、プログラムを安全に保つために、すべての不測の事態を考慮する必要があります。これにより、単純なシングルスレッドのワークフローと比較して、設計にかかる労力が大幅に増加します。

個別のデータセットでの作業や、スレッドを使用するように明示的に最適化されたプログラムで、スレッドがどのように優れているかを完全に理解できます。しかし、プログラマーが機能する安全なプログラムの出荷のみに関心がある「一般的な」ケースでは、ポーリングよりもスレッドを使用する理由を見つけることができません。

しかし、「セパレートスレッド」アプローチは非常に普及しているため、何かを見落としているのかもしれません。私を啓発します!:)

4

2 に答える 2

4

ソケットでスレッドを使用する一般的な理由は 2 つあります。

正当な理由: コンピュータに複数の CPU コアがあり、追加のコアを利用したいからです。シングルスレッド プログラムは 1 つのコアしか使用できないため、ワークロードが重い場合、1 つのコアが 100% 固定され、他のコアは使用されずに無駄になります。

あまり良くない理由: ブロック I/O を使用してプログラムのロジックを簡素化したい場合。特に、部分的な読み取りと部分的な書き込みの処理を避け、ソケットのスタックに各ソケットのコンテキスト/状態を保持したい場合。関連付けられているスレッド。ただし、低速のクライアント A が I/O 呼び出しをブロックして高速のクライアント B の処理を​​遅らせることなく、一度に複数のクライアントを処理できるようにする必要もあります。

2 番目の理由が良くない理由は、ソケットごとに 1 つのスレッドを使用するとプログラムの設計が単純化されているように見えますが、実際には通常は複雑になるからです。競合状態やデッドロックの可能性が生じ、共有データへの安全なアクセスが困難になります (前述のとおり)。さらに悪いことに、I/O のブロックに固執すると、スレッドは通常 I/O でブロックされるため、プログラムをきれいにシャットダウンする (またはスレッドのソケット以外の場所からスレッドの動作に何らかの影響を与える) ことが非常に難しくなります。 O コール (無期限の可能性もある) で、確実にウェイクアップする方法がない。(シグナルはマルチスレッド プログラムでは確実に機能しません。ノンブロッキング I/O に戻ると、期待していた単純化されたプログラム構造が失われます)

要するに、私はcibに同意します-マルチスレッドサーバーは問題を引き起こす可能性があるため、複数のコアを絶対に使用する必要がない限り、通常は避ける必要があります-それでも安全のために、複数のスレッドではなく複数のプロセスを使用する方がよい場合があります日本酒。

于 2012-06-27T23:55:43.627 に答える
2

スレッドの最大の利点は、累積された遅延時間が要求を処理するのを防ぐことです。ポーリングするときは、ループを使用して、状態が変化したすべてのソケットにサービスを提供します。少数のクライアントの場合、これはあまり目立ちませんが、非常に多数のクライアントを処理する場合、大幅な遅延につながる可能性があります。

各トランザクションには前処理と後処理が必要であると仮定します (プロトコルによっては、これは些細な量の処理である場合もあれば、BEEP や SOAP の場合のように比較的重要な場合もあります)。リクエストの前処理/後処理にかかる時間を合計すると、保留中のリクエストのバックログが発生する可能性があります。

説明のために、リクエストの前処理、処理、および後処理ステージがそれぞれ 1 マイクロ秒を消費し、合計リクエストが完了するまでに 3 マイクロ秒かかると想像してください。シングル スレッド環境では、受信リクエストが 1 秒あたり 334 リクエストを超えると、システムは圧倒されます (1 秒間に受信したすべてのリクエストを処理するのに 1.002 秒かかるため)。ただし、システムがスレッドを使用している場合、理論的には、受信したすべての要求を 1 秒で完了するのに必要な処理時間は 0.336 秒 * (共有データ アクセスの 0.334 + 前処理 0.001 + 後処理 0.001) だけです。期間。

理論的にはすべてのリクエストを 0.336 秒で処理することは可能ですが、これには各リクエストに独自のスレッドが必要になります。より合理的には、前処理と後処理の合計時間 (0.668 秒) を要求の数で乗算し、構成されたスレッドの数で割ります。たとえば、同じ 334 の着信要求と処理時間を使用すると、理論的には 2 つのスレッドがすべての要求を 0.668 秒 (0.668 / 2 + 0.334) で完了し、4 つのスレッドが 0.501 秒で、8 つのスレッドが 0.418 秒で完了します。

デーモンが受け取る最大要求量が比較的少ない場合は、ノンブロッキング I/O を備えたシングル スレッド実装で十分ですが、大量の要求がときどきバーストすることが予想される場合は、マルチスレッド モデルを検討する価値があります。

私は比較的スループットの低い UNIX デーモンをいくつか書いてきましたが、簡単にするためにシングルスレッドを使用しました。しかし、私が ISP 向けのカスタム netflow レシーバーを作成したとき、デーモンにスレッド化されたモデルを使用し、システム負荷平均の最小限の増加でインターネット使用のピーク時間を処理することができました。

于 2012-06-27T23:58:47.583 に答える