3

そのように定義されたサービスがある場合:

[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface IMyService
{
    [OperationContract(IsOneWay = true)]
    [ReceiveContextEnabled(ManualControl = true)]
    void DoSomething(Message<XElement> message);
}

クライアントから非同期で呼び出したい (svcutil から生成されない共有コントラクトを使用するか、サービス参照を追加する) ことができます。

Task task = Task.Factory.StartNew(() => myService.DoSomething(message));

... some other code

task.Wait();

サービスを非同期に定義することもできます。

[ServiceContract(SessionMode = SessionMode.NotAllowed)]
public interface ICacheKeyExchangeAsync
{
    [OperationContract(IsOneWay = true, AsyncPattern = true)]
    [ReceiveContextEnabled(ManualControl = true)]
    IAsyncResult BeginDoSomething(Message<XElement> message, AsyncCallback callback, object state);
    void EndDoSomething(IAsyncResult result);
}

代わりにこれを行う

IAsyncResult result = myService.BeginDoSomething(message, null, null);

.... some other code

myService.EndDoSomething(result);

アプローチ間に大きな違いはありますか?

4

2 に答える 2

12

はい、スレッド プールのスレッド使用率に違いがあります。

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
于 2013-06-29T13:29:04.400 に答える
1

OneWay = true

この属性を使用するOneWayと、クライアントはサービスがメソッドの実行を完了するまで待機しません。何もしないで待機するサービス メソッドを作成することで、簡単にテストできます。クライアントはサービス メソッドを (同期的にも) 呼び出して先に進みます。

サービスに簡単なテスト メソッドを記述することで、非常に簡単にテストできます。

    public void Test()
    {
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(30));
    }

属性の有無にかかわらず呼び出したときの動作を確認しOneWayます。そのため、メソッドを非同期で呼び出すことはやや無意味OneWayですが、そうすることは、非常に小さなこと (リクエストの作成や送信するデータの送信など) を別のスレッドにプッシュすることを意味すると思われるため、それでも役立つ可能性があります。

AsyncPattern = true

これは、クライアントに操作が終了するまで待機させたい場合 (たとえば、別の操作を開始する前) に便利です。の場合OneWay、クライアントはリクエストを送信し、それを忘れます - 何が起こっているかは気にしません。を使用するAsyncPatternと、サービスがメソッドの実行を終了すると、クライアントは通知を待ちます。

このパターンにはもう 1 つの利点があります。必要に応じて、サービスでのメソッドの実行が終了したときにコードを実行できます。DuplexServiceたとえば、クライアント ハンドラを管理し、特定のイベントが発生したときにクライアントに通知を送信する必要がある を作成する場合に便利です。

PS。あなたの投稿のこの部分に関しては、「svcutil から生成されない共有コントラクトを使用するか、サービス参照を追加する」について少し確信が持てません。私の回答には関係ないと思いますが、念のため、この免責事項をここに残します。;)

于 2013-06-27T08:55:45.383 に答える