4

この問題についてはかなりの数の議論がありましたが、彼らは私の特定の問題を説明できないようです。Threadクラスの代わりにThreadPoolを使用してスレッド化すると、パフォーマンスに深刻な問題が発生します。

詳細:

私はtcpサーバーを構築しました。tcpサーバーが新しいクライアントを受け入れると、そのクライアントを処理するための新しいスレッドが生成されます。すべてかなり単純ですが、サーバーが多くの同時クライアントを処理するには時間がかかりすぎます。2048バイトのバッファを送信して受信して閉じるだけの約35の単純なクライアントの場合は30秒。

ThreadPool.QueueUserWorkItem何度もストップワスをした後、最大26秒かかることがわかりました。新しいクライアントを処理するための新しいスレッドを生成するために使用します。交換後ThreadPool.QueueUserWorkItemnew Thread()パフォーマンスは1秒未満に向上しました。

なぜこれが起こっているのかについての説明が欲しいです。

明確化:

ThreadPool.QueueUserWorkItemが呼び出されてからclientMsgHandler.HandleIncomingMsgsが開始されるまで、遅延はクライアントコードとは関係ありません。20秒が経過する可能性があります。

ラグは最初のスレッドから始まり、テストが続くにつれて実際にはわずかに改善されます。私は解決策にはあまり興味がなく、なぜそれが起こっているのかについての説明にもっと興味があります。クライアントはブロックしますが、非常に短い間です。

サーバーコード:

private void AddTcpClientMsgHandler(TcpClient tcpClient)
    {
        //lock so no addition of client and closure can occur concurrently
        Stopwatch watch = new Stopwatch();
        watch.Start();
        Monitor.Enter(this);
        int pWatchIdx =  watchIDX++;
        if (!isOpen)
            throw new ObjectDisposedException(ResourceAlreadyClosed);

        TcpClientMsgHandler clientMsgHandler = CreateClientHandler(tcpClient);                                         
        clientMsgHandlerManager.AddTcpClientMsgHandler(clientMsgHandler);
        //ThreadPool.QueueUserWorkItem(clientMsgHandler.HandleIncomingMsgs); takes 20 seconds to run
        Thread thread = new Thread(clientMsgHandler.HandleIncomingMsgs);
        thread.Start();
        watch.Stop();
        Monitor.Exit(this);
        Console.WriteLine(string.Format("Iteration {0} took {1} Client {2}", pWatchIdx.ToString(),watch.Elapsed, tcpClient.Client.RemoteEndPoint));

    }
4

4 に答える 4

4

ブロッキングコードはThreadPoolの敵です。投稿した例からは、ブロックが発生している場所を特定することはできませんが、コードパスを確認して、コードがブロックされている場所を見つけることをお勧めします。サーバーが高遅延を示し始めるまでデバッガーでサーバーを実行し、実行を中断して、VSのスレッドパネルを確認します。これにより、スレッドがブロックされた場所が表示されます。ほとんどの場合、これは同期IOにあります。非同期コードに置き換えることを検討してください。

于 2013-01-15T10:29:55.943 に答える
1

ThreadPool.QueueUserWorkItem-実行のためにメソッドをキューに入れます。このメソッドは、スレッドプールスレッドが使用可能になると実行されます。

  • ThreadPoolは、アイドル状態のスレッドを待機します。
  • アイドル状態のスレッドが見つかると、ThreadPoolはそれを使用してメソッドを実行します。

なぜThreadPoolは遅いのですか?

  • ThreadPoolのスレッドが不足するまで、上記の2つは実際には理由ではありません。
  • .NET 3.5では、2000のワーカースレッドと1000のIO完了ポートスレッドがあります。(4.0,4.5ではさらに多く)。Jon Skeetの回答(スレッドプール内のアクティブなスレッド番号)を確認してください。

[例えば]

.Net 2.0は、使用可能なプロセッサごとにデフォルトで25スレッドになります。これは、30個のタスクをキューに入れる場合、最後の5個は、実行される前にスレッドがプールから使用可能になるのを待つ必要があることを意味します。

解決 :

SetMinThreads()を使用して、スレッドの最小数を30にします(.Net 2.0の場合)。ThreadPoolは必要なときにすぐに新しいスレッドを作成しないため、これによりパフォーマンスが向上します。これは特定の間隔でのみ実行されます。

注:クライアントごとに1つのスレッドを使用しても、それ以上の同時実行はサポートされません。

非同期ソケットを使用する-ポーリングする必要がないことを除いて、これらは非ブロッキングソケットです。スタックは、何か「興味深い」ことが発生するたびに、プログラムに特別なウィンドウメッセージを送信します。

于 2013-01-15T11:34:00.460 に答える
0

スレッドプールの最初の数スレッドが使い果たされた後、システムは新しいスレッドプールスレッドを開始する前に遅延を導入します(これは再利用されたスレッドには影響しません)。

スレッドを開始する前に、 ThreadPool.SetMinThreadsを適切な大きさに設定することで、スレッドプールスレッドの初期数を変更できます。しかし、あなたはそれをすることを想定されていません!(だからあなたは私からそれを聞いていませんでした...;)

それを行う代わりに、スレッドの数を減らす方法を検討する必要があります。

于 2013-01-15T10:24:28.643 に答える
0

それはあなたがあなたの方法で何をしているのかによると思いますclientMsgHandler.HandleIncomingMsgs()。スレッドプールは、本当に短い処理にのみ使用する必要があります。

さらに、スレッドプールのデフォルトサイズは、使用可能なプロセッサごとに25ワーカースレッドです。スレッドのクロスロックに注意してください。

>>管理されたスレッドプール

于 2013-01-15T10:35:23.777 に答える