在我的示例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);
在上面的例子中,它按顺序调用每个操作,但如果我选择并发模式为“Multiple”而不是“Single”,它将同时执行操作。
我在许多地方都读到'ConcurrencyMode对PerCall服务没有影响,因为根据定义,来自客户端的每个调用都会被分配一个带有自己线程的新服务实例:PerCall总是有效的ConcurrencyMode.Single。'
我正在使用netTcpBinding绑定。
问题确实让我感到害怕,请告诉我这个行为。
谢谢!
答案 0 :(得分:2)
如果我们使用PerCall实例化,并发模式是否相关?
通常不是。如果您总是为ConcurrencyMode.PerCall
的每个调用创建一个新实例,则每个实例中只有一个线程...因此本身就是线程安全的。
唯一的例外是,如果服务“A”对服务“B”进行同步呼叫,则服务“B”将呼叫返回到服务“A”的同一实例。这特别是ConcurrencyMode.Reentrant案例。
查看您发布的程序输出:
令人困惑的是您正在使用相同的代理对象。序列化在客户端而不是服务上。代理是线程安全的,但一次只向服务发送一个请求。
我在下面写了一个命令行程序,你可以运行:
当我在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行为。