1

私のサンプル WCF コンソール アプリケーションでは、サービス クラスのサービス動作を次のように設定しました。

[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single, InstanceContextMode=InstanceContextMode.PerCall)]

以下に示すように、同じプロキシ オブジェクトを使用してサービスの複数の操作を呼び出します。

             proxy = new ConcurrencyServiceClient("ConcurrencyService_Tcp");

             proxy.BuyStock("MSFT", 100);

             proxy.BuyStock("GOOG", 50);

             proxy.BuyStock("F", 500);

             proxy.SellStock("AAPL", 100);

             proxy.SellStock("ORCL", 300);

             proxy.SellStock("GM", 75);

上記の場合、各操作を順番に呼び出していますが、同時実行モードを「単一」ではなく「複数」として選択すると、操作が同時に実行されます。

「定義上、クライアントからの各呼び出しには独自のスレッドを持つ新しいサービス インスタンスが割り当てられるため、ConcurrencyMode は PerCall サービスには影響しません。PerCall は常に実質的に ConcurrencyMode.Single です。」</p>

netTcpBinding バインディングを使用しています。

問題は本当に私を殺しています。この動作について教えてください。

ありがとう!

4

1 に答える 1

2

PerCall インスタンス化を使用する場合、同時実行モードは適切ですか?

通常はありません。を使用してすべての呼び出しに対して常に新しいインスタンスを作成している場合ConcurrencyMode.PerCall、各インスタンスには単一のスレッドしかありません...したがって、本質的にスレッドセーフです。

唯一の例外は、サービス "A" がサービス "B" に同期呼び出しを行い、次にサービス "B" がサービス "A" の同じインスタンスに呼び出しを戻す場合です。これは具体的にはConcurrencyMode.Reentrantのケースです。

あなたが投稿したプログラム出力を見る:

混乱は、同じプロキシ オブジェクトを使用していることです。シリアライゼーションは、サービスではなくクライアント上で行われます。プロキシはスレッド セーフですが、一度に 1 つの要求しかサービスに送信しません。

あなたが実行できる以下のコマンドラインプログラムを書きました:

  • サービス メソッドは、戻る前に 1 秒間スリープします。
  • クライアント メソッドはループを 2 回実行します。
    • 各ループ内で、サービスを 10 回呼び出すために 10 個のスレッドが作成されます。
    • 最初のループでは、呼び出しごとに新しいプロキシが作成され、同時実行性が確認されます。
    • 2 番目のループでは、スレッド間で同じプロキシを共有し、呼び出しがシリアル化されます。

ConcurrencyMode.Multiple と ConcurrencyMode.Single を切り替えても、出力に違いは見られません。

using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading.Tasks;

using System.Collections.Generic;

namespace ConsoleWCF
{
    [ServiceContract]
    public interface ISimple
    {
        [OperationContract]
        int GiveItBack(int i);
    }

    //// Different ConcurrencyMode does NOT change the output.
    //[ServiceBehavior(ConcurrencyMode=ConcurrencyMode.Single, InstanceContextMode=InstanceContextMode.PerCall)]
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerCall)]
    public class SimpleService : ISimple
    {
        public int GiveItBack(int i)
        {
            Console.WriteLine("Return " + i);
            System.Threading.Thread.Sleep(1000);
            return i;
        }
    }

    public static class Program
    {
        static void Main(string[] args)
        {
            ServiceHost simpleHost = new ServiceHost(typeof(SimpleService), new Uri("http://localhost/Simple"));
            simpleHost.Open();

            ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(simpleHost.Description.Endpoints[0]);
            List<Task> tasks = new List<Task>();


            Console.WriteLine("{0}{0}Start proxy per call....", Environment.NewLine);
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            for (int i = 0; i < 10; i++)
            {
                int value = i;
                tasks.Add(Task.Factory.StartNew(() =>
                    {
                        // proxy per call...
                        ISimple proxy = factory.CreateChannel();    // <-------- new Proxy
                        Console.WriteLine("Client    - Sending " + value);
                        int response = proxy.GiveItBack(value);
                        Console.WriteLine("Client    - Got back " + response);

                        ((ICommunicationObject)proxy).Shutdown();
                    }));
            }

            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Finished in {0} msec", stopwatch.ElapsedMilliseconds);

            Console.WriteLine("{0}{0}Start Shared proxy....", Environment.NewLine);
            ISimple sharedProxy = factory.CreateChannel();    // <-------- one one Proxy
            stopwatch = System.Diagnostics.Stopwatch.StartNew();

            for (int i = 0; i < 10; i++)
            {
                int value = i;
                tasks.Add(Task.Factory.StartNew(() =>
                {
                    // proxy per call...
                    Console.WriteLine("Client    - Sending " + value);
                    int response = sharedProxy.GiveItBack(value);
                    Console.WriteLine("Client    - Got back " + response);
                }));
            }

            Task.WaitAll(tasks.ToArray());

            ((ICommunicationObject)sharedProxy).Shutdown();

            Console.WriteLine("Finished in {0} msec", stopwatch.ElapsedMilliseconds);

            Console.WriteLine("Press ENTER to close the host once you see 'ALL DONE'.");
            Console.ReadLine();

            factory.Shutdown();
            simpleHost.Shutdown();
        }
    }

    public static class Extensions
    {
        static public void Shutdown(this ICommunicationObject obj)
        {
            try
            {
                obj.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine("Shutdown exception: {0}", ex.Message);
                obj.Abort();
            }
        }
    }
}

出力例:

Start proxy per call....
Client    - Sending 0
Client    - Sending 1
Client    - Sending 2
Client    - Sending 3
Return 1
Return 2
Return 0
Return 3
Client    - Sending 4
Return 4
Client    - Got back 2
Client    - Got back 1
Client    - Got back 0
Client    - Got back 3
Client    - Sending 5
Client    - Sending 6
Client    - Sending 7
Client    - Sending 8
Return 5
Return 6
Return 7
Return 8
Client    - Sending 9
Client    - Got back 4
Return 9
Client    - Got back 6
Client    - Got back 8
Client    - Got back 5
Client    - Got back 7
Client    - Got back 9
Finished in 3009 msec


Start Shared proxy....
Client    - Sending 0
Client    - Sending 3
Client    - Sending 5
Client    - Sending 2
Client    - Sending 1
Client    - Sending 4
Return 0
Client    - Sending 6
Client    - Got back 0
Client    - Sending 7
Return 3
Client    - Sending 8
Client    - Got back 3
Client    - Sending 9
Return 5
Client    - Got back 5
Return 2
Client    - Got back 2
Return 1
Client    - Got back 1
Return 4
Client    - Got back 4
Return 6
Client    - Got back 6
Return 7
Client    - Got back 7
Return 8
Client    - Got back 8
Return 9
Client    - Got back 9
Finished in 10027 msec
Press ENTER to close the host once you see 'ALL DONE'.

個別のプロキシを作成するループの実行に 1 秒ではなく 3 秒かかるのは、おそらくどこかでスロットルまたはスレッドの制限が原因です...それを制限している WCF の動作ではありません。

于 2013-10-08T21:41:20.437 に答える