11

私は、シミュレートされたすべてのクライアントがサーバーに対して事前定義されたルーチンを実行するクライアント シミュレーション プログラムを作成しています。これは、4 つのインスタンスで Azure で実行されている Web サーバーです。

シミュレートされたすべてのクライアントは、サーバーに接続した後、同じルーチンを実行します。

いつでも、私のプログラムを使用して 300 から 800 のクライアントをシミュレートしたいと考えています。

私の質問は、クライアント クラスの N インスタンスを作成し、それらを N 個の異なるスレッドで実行する必要があるかどうかです。また

そのためにタスク ライブラリを使用する必要がありますか?

4

4 に答える 4

23

800 スレッドを作成するべきではありません。

ここで一歩戻りましょう。「クライアント」から「要求」を受け取り、それらのクライアントに「応答」を送り返す「サーバー」と呼ばれるデバイスがあります。要求が郵便局から配達される紙切れであり、応答が郵便局から配達される本が入った箱であるとします。

サーバーをテストするために、800 台のクライアントをシミュレートしたいと考えています。

スレッドが人で、プロセッサーが椅子だとしましょう。人は椅子に座っている間しか仕事をすることができません。

800 のスレッドを作成することは、外に出て 800 人を雇い、それぞれにお金を払ってサーバーに手紙を送ることに相当します。しかし、椅子が 4 つしかないので、800 人が交代で椅子を使用する必要があります。

それは実生活ではばかげた解決策です。スレッドは、人間と同じように、非常に高価です。作成するスレッドの数を最小限に抑える必要があります。

では、代わりにタスク ファクトリを介して 800 のタスクを作成し、TPL にそれらを並列化させるべきでしょうか?

いいえ、あなたもそうすべきではありません。TPL には、引き出される人々 (スレッド) のプールがあり、従業員が座る椅子よりも多くの人が給与に含まれないように物事を調整しようとします。しかし、あなたの仕事は「椅子に縛られている」わけではありません - - 人々は椅子に座り、リクエストをサーバーに送信し、応答が返ってくるのを待つ間に椅子から出ます。彼らが待っている間、TPL は追加のタスクを処理するためにさらに多くの人を雇う必要があります。

Web サーバーへのアクセスは I/O バウンドです。CPU バウンドのタスクに対してのみスレッド プール タスクを作成する必要があります。

正しい解決策は、 2人を雇うことです。

1 人 (「I/O 完了スレッド」) は、メールボックスに要求をドロップし、着信パッケージをチェックするだけです。もう 1 人の「シミュレーション」担当者は、800 人のクライアントをシミュレートするための適切な「スケジュール」を考え出します。シミュレーション担当者は、スケジュールを立てて就寝します。サーバーに別のリクエストを送信する時間になると、彼女は目を覚まします。彼女が目を覚ますと、彼女は I/O 完了スレッドにこの手紙をメールボックスにドロップするように指示し、応答が来たら彼女を起こします。その後、彼女は別の要求を送信する時間になるまで、または応答が来るまでスリープ状態に戻ります。確認する必要があります。

あなたがすべきことは、(1) C# 5 のベータ版を入手し、それを使用async/awaitしてサーバーに要求を送信するタスクを作成し、別の要求を送信する時間になるか応答が来るまで、制御をメッセージ ループに戻します。または、C# 5 を使用したくない場合は、タスク完了ソースを作成し、適切な継続を持つタスクを設定する必要があります。

要するに、多くの並列 I/O タスクを処理する正しい方法は、それぞれが一度に非常に少量の作業を行う非常に少数のスレッドを作成することです。I/O 完了スレッドに I/O の詳細を処理させます。800 通の手紙を送ることをシミュレートするために、800 人を雇う必要はありません。2人を 雇って、1 人はメールボックスを監視し、もう 1 人は手紙を書きます。

于 2012-04-05T19:13:51.847 に答える
2

この場合の答えはそれほど単純ではありません。これは、クライアントをどのようにシミュレートするかによって異なります。

  1. 800のクライアントを接続したいが、必ずしも同時にではない場合は、を使用することをお勧めしますTask。それらは軽量で、基礎となるを効率的に利用しますThreadPool

  2. クライアントを完全に並列にしたいのであれば、実際にスレッドを回避する方法はないのではないかと思います。800の軽量の同時実行タスクを取得する魔法の方法はありません。スレッドプールを使用するため、Task抽象化は軽量です。これは、多くのタスクが少数の実際のスレッドにマップされることを意味します。しかし、もちろん、これは、それらが実際には並列で実行されるのではなく、可能な限り実行されるようにスケジュールされていることを意味します。のThreadPoolスレッドの最大数は250(AFAIK)であるため、sを使用した場合、実際に一度に実行される「クライアント」は250を超えませんTask。解決策は最大スレッド数を800に設定しますが、この時点では、従来のスレッドを使用するのと同じです。

于 2012-04-05T18:51:49.783 に答える
1

このアプリケーション ドメインでは、最善の策です。

アプリケーション ドメインは、.NET アプリケーションが実行される実行時の分離単位です。管理されたメモリ境界、アプリケーション構成設定のコンテナ、および分散アプリケーションの通信インターフェイスを提供します。

通常、各 .NET アプリケーションは、特定のプロセス/プログラムの開始時に CLR によって自動的に作成されるアプリケーション ドメインを 1 つだけホストします。単一のプロセス/プログラムで追加のアプリケーション ドメインを作成すると便利な場合があります (あなたの場合など)。複数のアプリケーション ドメインを使用すると、通信の複雑さが回避され、いくつかの個別のプロセスを使用して発生し、タスクが分離されます。

あなたが望むものには、2つのオプションがあります。

  1. 同じドメイン内の別のスレッドで X スレッドを開始します。

これは、複数のログインのシミュレート、クライアントのシミュレートなどのタスクでは非常に困難になる、スレッドセーフであることに非常にうんざりしなければならないことを意味します.

  1. それぞれ独自のアプリケーション ドメイン内の同じプロセスで X スレッドを開始します。

これにより、スピンされた各スレッドが分離され、ホスティング アプリケーション/プログラムから簡単にアクセスできるようになります。すべての X シミュレーションを X 個のアプリケーション ドメインで行うことにより、各ドメインは分離され、静的クラス メンバーなどを介して別のクライアント シミュレーションに干渉することができなくなります。

以下は、Joseph Albahari の著書C# 4.0 In a Nutshellからの抜粋を参考にしています。入手することを強くお勧めします。

40 の同時クライアント シミュレーションの例が役立つ場合があります。

class program
{
    static void main()
    {
        // Create 40 domains and 40 threads.
        AppDomain[] domains = new AppDomain[40];
        Thread[] thread = new Thread[40];

        for (int i = 0; i < 40; i++)
        {
            domains[i] = AppDomain.CreateDomain("Client Simulation " + i);
            thread[i] = new Thread(SimulateClientInOtherDomain);
        }

        // Start all threads, passing to each thread its app domain.
        for (int j = 0; j < 40; j++)
            threads[j].Start(domains[j]);

        // Wait for the threads to finish.
        for (int k = 0; k < 40; k++)
            threads[k].Join();

        // Unload the application domains.
        for (int l = 0; l < 40; l++)
            AppDomain.Unload(domains[l]);
    }

    // Thread start with input of with domain to run on/in.
    static void SimulateClientInOtherDomain(object domain)
    {
        ((AppDomain)domain).DoCallBack(Simulate);
    }

    static void Simulate()
    {
       Client simClient1 = new Client("Bill", "Gates", ...);
       simClient1.Simulate();
    }
}

これが役立つことを願っています。

于 2012-04-05T18:56:56.953 に答える
1

タスク ライブラリを使用して、タスク ライブラリにすべてのスレッドを処理させます。800 スレッドをスピンアップしたくありません。一度に多くの同時スレッドを実行するのは悪い考えです。これについて説明する別のスタック オーバーフローの質問があります: .NET アプリのスレッドの最大数は?

于 2012-04-05T18:12:35.577 に答える