0

C# で TcpClient クラスを使用しています。

新しい tcp 接続要求があるたびに、通常はそれを処理する新しいスレッドを作成します。また、メイン スレッドがこれらのハンドラ スレッドをいつでも終了できるようにする必要があります。

これらの各ハンドラ スレッドに対する私の解決策は次のとおりです。

1 Check NetworkStream's DataAvailable method
    1.1 If new data available then read and process new data
    1.2 If end of stream then self terminate
2 Check for terminate signal from main thread
    2.1 If terminate signal activated then self terminate
3 Goto 1.

このポーリング アプローチの問題点は、これらのハンドラ スレッドのすべてが大量のプロセッサ リソースを占有することであり、これらのスレッドが膨大な数にある場合は特にそうです。これにより、非常に非効率になります。

これを行うより良い方法はありますか?

4

4 に答える 4

2

リクエストごとに新しいスレッドを作成せずに、「.NET 方式」でこれを行う方法については、非同期サーバー ソケットの例を参照してください。

于 2009-12-08T02:58:34.620 に答える
1

信じられないかもしれませんが、1000 ティックのスリープで物事がスムーズに進むとは限りません。

private readonly Queue<Socket> sockets = new Queue<Socket>();
private readonly object locker = new object();
private readonly TimeSpan sleepTimeSpan = new TimeSpan(1000);
private volatile Boolean terminate;

private void HandleRequests() 
{
    Socket socket = null;

    while (!terminate)
    {
        lock (locker)
        {
            socket = null;
            if (sockets.Count > 0)
            {
                socket = sockets.Dequeue();
            }
        }

        if (socket != null)
        {
            // process
        }

        Thread.Sleep(sleepTimeSpan);
    }   
}
于 2009-12-08T03:07:51.720 に答える
0

同様の種類の Windows サービスに取り組んだことを覚えています。これは、約 1000 の TCP 接続を取り、データを NTRIP キャスターにルーティングできる NTRIP サーバーでした。

このアプリケーション専用のサーバーがある場合は、各スレッドにさらにコードを追加しない限り問題にはなりません (ファイル IO、データベースなど - 私の場合は、接続ごとにログイン/ログアウトするためのデータベース処理もありました) .

注意すべきこと:

  1. スレッドが最大 600 程度になるときの帯域幅。何らかの理由で TCP バッファ ウィンドウが詰まるか、使用可能な帯域幅が不足すると、切断が見られるようになります。
  2. このアプリケーションを実行しているオペレーティング システムにはいくつかの制限があり、切断が発生する可能性があります

上記はあなたの場合には当てはまらないかもしれませんが、開発中に直面したので、ここに入れたかっただけです。

于 2009-12-08T02:49:21.297 に答える
0

すべてのスレッドを「ビジー待機」(つまり、小さなループを何度も実行) させたくないというのは正しいことです。それらをブロックするか、非同期 I/O を使用する必要があります。

John Saunders が述べたように、非同期 I/O は数百の接続にスケールアップできるため、これを行うための「正しい方法」です。基本的に、BeginRead() を呼び出してコールバック関数に渡します。BeginRead() はすぐに戻り、データが到着すると、コールバック関数がスレッド プールからスレッドで呼び出されます。コールバック関数はデータを処理し、BeginRead() を再度呼び出してから戻り、スレッドを解放してプールに戻します。

ただし、一度に少数の接続しか開いていない場合は、接続ごとにスレッドを作成してもまったく問題ありません。ループで DataAvailable プロパティをチェックする代わりに、先に進んで Read() を呼び出します。データが読み取れるようになるまで、スレッドはブロックされ、CPU を消費しません。接続が失われた場合、または別のスレッドから閉じた場合、Read() 呼び出しは例外をスローします。これは、リーダー スレッドを終了することで処理できます。

于 2009-12-08T03:02:40.783 に答える