はい、スレッド プールのスレッド使用率に違いがあります。
CLR スレッド プールは、スレッドをワーカーと I/O の 2 つのタイプに分割します (詳細については、 「. NETおよびMSDNのワーカー スレッドと I/O スレッドの簡単な説明」を参照してください)。一般的に言えば、スレッド プールはコアあたり 250 のワーカー スレッドと 1000 の I/O スレッドを提供するため、ワーカー スレッドを使用して WCF サービス入力を処理し、I/O スレッドを使用して非同期の送信/受信操作の完了 (サポートされている) を待機できます。重複した I/Oメカニズムによる Windows OS レベルで)。
上記を念頭に置いて、 ThreadPool.GetAvailableThreads()メソッドを使用して、両方のアプローチで使用されているスレッドを見てみましょう。
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
ここではクライアント側のスレッド プールの使用率の結果のみを示しますが、サーバー側でも同様です。
一方向の WCF 操作に対する APM アプローチ。
WCF 契約の場合:
[ServiceContract]
public interface IService1
{
[OperationContract(IsOneWay = true, AsyncPattern = true)]
IAsyncResult BeginDoSomething(int value, AsyncCallback callback, object state);
void EndDoSomething(IAsyncResult result);
}
次のコードを使用して、クライアントからサーバーに 100 個のリクエストを送信します。
ChannelFactory<IService1> channelFactory = new ChannelFactory<IService1>();
var client = channelFactory.CreateChannel();
for (int i = 0; i < 100; i++)
{
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
client.BeginDoSomething(i, asyncCallback, null);
}
出力は次のとおりです。
1023 worker and 1000 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 996 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
ご覧のとおり、私の x4 コア マシンではすべてのワーカー スレッドが利用可能で、いくつかの I/O スレッドが使用されています。
TPL タスクとして同期一方向操作を実行しています。
WCF 契約の場合:
[ServiceContract]
public interface IService2
{
[OperationContract(IsOneWay = true)]
void DoSomething(int value);
}
次のコードを使用して、クライアントからサーバーに 100 個の要求を実行してみましょう (TPL が内部で CLR ThreadPool を使用していることに注意してください)。
for (int i = 0; i < 100; i++)
{
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
Task.Run(() => client.DoSomething(i));
}
出力は次のとおりです。
1023 worker and 1000 I/O threads are available
1022 worker and 1000 I/O threads are available
1021 worker and 1000 I/O threads are available
1020 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
1019 worker and 1000 I/O threads are available
ご覧のとおり、ワーカー スレッドは使用されていますが、I/O スレッドは使用されていません。
それで、推奨されるアプローチは何ですか?
要約すると、ソリューションは次のようにする必要があります。
- スレッド プールのワーカー スレッドと I/O スレッドを利用して (特に負荷の高いアプリケーションの場合)、ボトルネックを回避します。
- 非同期操作を Task にラップすることで、TPL と新しい C# の async/await 機能のすべての利点を得ることができます。
- OneWay 操作を非同期で実行することは絶対に合理的です ( OneWay が実際には OneWay でない場合があることを考慮してください)。
したがって、推奨されるアプローチは、上記のすべての要件を満たすWCF のタスクベースの非同期パターンです。
WCF のタスク ベースの非同期パターン。
契約の場合:
[ServiceContract]
public interface IService3
{
[OperationContract(IsOneWay = true)]
Task DoSomethingAsync(int value);
}
もう一度 100 個のリクエストを送信します。
for (int i = 0; i < 100; i++)
{
int worker;
int ioCompletion;
ThreadPool.GetAvailableThreads(out worker, out ioCompletion);
Console.WriteLine("{0} worker and {1} I/O threads are available", worker, ioCompletion);
client.DoSomethingAsync(i);
}
出力:
1023 worker and 1000 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available
1023 worker and 998 I/O threads are available
1023 worker and 999 I/O threads are available