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 の動作ではありません。