私はすべてのシミュレートされたクライアントがサーバーに対していくつかの事前定義されたルーチンを実行するクライアントシミュレーションプログラムを書いています - それは4つのインスタンスで紺碧で実行されているウェブサーバーです。
シミュレートされたすべてのクライアントは、サーバーに接続した後に同じルーチンを実行します。
いつでも私は自分のプログラムを使って300から800のクライアントをシミュレートしたいと思います。
私の質問は: クライアントクラスのインスタンスをN個作成し、それらをN個の異なるスレッドで実行する必要がありますか?または
そのためにタスクライブラリを使うべきですか?
あなたは確かに800スレッドを作るべきではありません。
ここで一歩戻りましょう。あなたは、 "クライアント"から "リクエスト"を受け取り、それらのクライアントに "レスポンス"を送り返す "サーバー"と呼ばれるデバイスを持っています。要求は郵便局によって配達された紙片であり、応答は郵便局によって配達された本を含む箱であると仮定しよう。
あなたがしたいシミュレートするサーバーをテストするために800クライアント。
スレッドが人で、プロセッサが椅子であるとしましょう。人は椅子に座っている間だけ仕事をすることができる。
800個のスレッドを作成することは、800人の従業員を出社させ、それぞれに支払いをしてサーバーに手紙を送ることと同じです。しかし、あなたは4つの椅子しか持っていないので、それらの800人はすべて椅子を使って交代しなければなりません。
それはおかしな実生活でのソリューション。人と同じように、スレッドはめちゃくちゃ高価。作成するスレッドの数を最小限に抑えるべきです。
それでは、代わりにタスクファクトリを介して800個のタスクを作成し、TPLにそれらを並列化させるべきですか。
いいえ、そうしないでください。 TPLはそこから引き出す人(スレッド)のプールを持っていて、それが着席するための椅子があるよりも多くの人が給与上にいないように物事を配置しようとします。しかしあなたの仕事は「椅子に縛られません」 - - 人々は椅子に座り、サーバーに要求を送り、そして応答が返ってくるのを待っている間に椅子から降ります。彼らが待っている間に、TPLは今や追加のタスクをサービスするためにより多くの人々を雇わなければなりません。
Webサーバーにアクセスすると、入出力が制限されます。スレッドプールタスクは、CPUバウンドのタスクに対してのみ作成してください。
正しい解決策は雇うことです二人。
一人の人 - "I / O完了スレッド" - はメールボックスにリクエストを落として着信パッケージをチェックする以外何もしません。もう一人の人(「シミュレーション」人)は、正しい「スケジュール」が800人のクライアントをシミュレートするためのものであることを解明します。シミュレーション担当者はスケジュールを立て、その後眠りにつきます。彼女は別の要求をサーバーに送信するときがきたときに起きます。彼女は目を覚ますと、I / O完了スレッドにこの文字をメールボックスに落とし、応答が入ったら目を覚ますように指示します。その後、別の要求を送信するか、応答を受け取るまでスリープ状態に戻ります。それが検証される必要があるのです。
(1)C#5のベータ版を入手して使うasync/await
サーバーに要求を送信するタスクを作成してから、別の要求を送信する時間または応答が受信されるまでメッセージループに制御を戻す。または、C#5を使用したくない場合は、作成する必要があります。タスク完了ソースをクリックし、正しい継続を持つタスクを設定します。
要するに、多くの並列I / Oタスクを処理する正しい方法は、非常に少数のスレッドを作成することです。各スレッドは一度に非常に少量の作業を行います。 I / O完了スレッドにI / Oの詳細を処理させます。800文字の送信をシミュレートするために800人を雇う必要はありません。雇う二メールボックスを見る人と手紙を書く人。
この場合の答えはそれほど単純ではありません。それは本当にあなたのクライアントがどのようにシミュレートされることを望むかに依存します:
800台のクライアントを接続したいが、必ずしも同時に接続したくない場合は、それを使用することをお勧めします。Task
s。それらは軽量で、基礎となるものを効率的に利用します。ThreadPool
。
あなたが本当にクライアントを完全にすべて並列にしたいのであれば、私は実際にスレッドを回避する方法がありません。 800の軽量な同時実行タスクを取得するための魔法のような方法はありません。のTask
抽象化はスレッドプールを使用するため、軽量です。これは、多くのタスクが少数の実際のスレッドにマップされることを意味します。しかし、もちろん、これは、それらが本当に並行して実行されるのではなく、可能な限り実行されるようにスケジュールされることを意味します。のThreadPool
最大スレッド数は250(AFAIK)なので、実際には一度に250を超える "クライアント"を実行することはできません。Task
s。解決策は最大スレッド数を800に設定していますが、この時点では従来のスレッドを使用するのと同じです。
私はタスクライブラリを使用し、タスクライブラリにすべてのスレッドを処理させます。あなたは800スレッドをスピンアップしたくありません。同時に多くのスレッドが同時に実行されるのは悪い考えです。これについて話している別のスタックオーバーフローの質問があります。.NETアプリの最大スレッド数は?
このためアプリケーションドメインあなたの最善の策です。
あアプリケーションドメイン.NETアプリケーションが実行されるランタイムの分離単位です。それは管理されたメモリ境界、アプリケーション構成設定のためのコンテナー、そして分散アプリケーションのための通信インターフェースを提供します。
各.NETアプリケーションは通常、特定のプロセス/プログラムの起動時にCLRによって自動的に作成されるアプリケーションドメインを1つだけホストします。単一のプロセス/プログラムで追加のアプリケーションドメインを作成すると便利な場合があります(あなたの場合など)。複数のアプリケーションドメインを使用すると、通信の複雑さを回避し、いくつかの個別のプロセスを使用して発生し、タスクを分離できます。
あなたが欲しいもののためにあなたは2つの選択肢があります。
これは、スレッドセーフであることに非常に用心深くなる必要があることを意味します。これは、複数のログインをシミュレートしたり、クライアントをシミュレートするなどのタスクでは非常に困難になります。
これにより、それぞれのスピンスレッドが分離され、ホスティングアプリケーション/プログラムからアクセスしやすくなります。すべてのXシミュレーションをXの別々のアプリケーションドメインで扱うことによって、各ドメインは分離され、静的クラスメンバーなどを介して他のクライアントシミュレーションを妨害することはできません。
以下はJoseph Albahariの本からの抜粋です。一言で言えばC#4.0私は強く得ることをお勧めします:
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();
}
}
これが助けになれば幸いです。