如果我们使用 PerCall 实例化,并发模式是否相关?
通常不会。如果您总是为每次调用创建一个新实例ConcurrencyMode.PerCall
,那么每个实例中只有一个线程......因此本质上是线程安全的。
唯一的例外是服务“A”对服务“B”进行同步调用,然后服务“B”对服务“A”的同一实例进行回调。这就是ConcurrencyMode.Reentrant的具体情况。
查看您发布的程序输出:
令人困惑的是您使用的是相同的代理对象。序列化在客户端,而不是服务上。代理是线程安全的,但一次只会向服务发送一个请求。
我在下面写了一个命令行程序,你可以运行它:
- 服务方法会在返回前休眠 1 秒
- 客户端方法执行两次循环。
- 在每个循环中,它创建 10 个线程来调用服务十次。
- 在第一个循环中,它为每个调用创建一个新代理 - 您会看到并发。
- 在第二个循环中,它跨线程共享相同的代理 - 并且调用被序列化。
当我在 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'.
为什么创建单独代理的循环仍然需要 3 秒而不是 1 秒才能运行,这可能是由于某处的油门或线程限制......它不是限制它的 WCF 行为。