重入与回调并发(Reentrant & CallbackConcurrency )
对于双工通讯来说,服务对客户端的回调也是通过代理完成的。那么这又涉及到另外一个问题:回调客户端时,能否让回调服务也并发执行呢?WCF中定义了CallbackBehaviorAttribute ,可以通过它来设置回调服务的行为。它同样定义了ConcurrencyMode,可指定回调的并发模式,但它没有定义回调的实例模式,即InstanceContextMode。本文主要探讨服务的并发与回调服务的并发。
目录:
- 测试重入与回调并发
- 会话对 重入与回调并发
- 设置服务并发的ServiceBehaviorAttribute能用于回调服务吗?
publicinterface IAdd
{
[OperationContract]
void Add(int x, int y);
}
{
[OperationContract]
void ShowResult(int result);
}
{
private readonly int counter;
public AddService()
{
counter++;
Console.ResetColor();
Console.WriteLine(string.Format("AddService Construction function excute... counter is {0}", counter));
}
#region IAdd 成员
public void Add(int x, int y)
{
var clientId = OperationContext.Current.IncomingMessageHeaders.GetHeader<int>(MessageWrapper.headerClientId,
MessageWrapper.headerNamespace);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(string.Format("Time:{0};ThreadId is :{1}.Request Id is {2} Add Method Invoked,", DateTime.Now,
Thread.CurrentThread.ManagedThreadId, clientId));
Thread.Sleep(5000);
int result = x + y;
MessageHeader<int> header = new MessageHeader<int>(clientId);
System.ServiceModel.Channels.MessageHeader messageHeader =
header.GetUntypedHeader(MessageWrapper.headerClientId,
MessageWrapper.headerNamespace);
OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
Console.WriteLine(string.Format("Time: {1} Requst Id {0} begin Callback",clientId,DateTime.Now));
IAddCallback callbackProxy= OperationContext.Current.GetCallbackChannel<IAddCallback>();
callbackProxy.ShowResult(result);
Console.WriteLine(string.Format("Time: {1} Requst Id {0} Callback finished", clientId, DateTime.Now));
Console.WriteLine(string.Format("result is : {0}",result));
Thread.Sleep(5000);
Console.WriteLine("=========Excute finished=========");
Console.WriteLine();
}
#endregion
}
<services>
<service name="Service.AddService">
<endpoint address="http://127.0.0.1:3636/AddService" binding="wsDualHttpBinding" contract="Contract.IAdd" ></endpoint>
</service>
</services>
</system.serviceModel>
{
private readonly int counter;
public AddCallback()
{
counter++;
Console.WriteLine(string.Format("AddCallback Construction function excute... counter is {0}", counter));
}
#region IAddCallback 成员
public void ShowResult(int result)
{
int callbackRequestId= OperationContext.Current.IncomingMessageHeaders.GetHeader<int>(MessageWrapper.headerClientId,
MessageWrapper.headerNamespace);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(string.Format("Time: {1} ThreadId is :{2} Callback RequestId : {0} 开始执行回调", callbackRequestId, DateTime.Now,Thread.CurrentThread.ManagedThreadId));
Thread.Sleep(10000);
Console.WriteLine(string.Format("Add result is {0}", result));
Console.WriteLine(string.Format("Time: {1} ThreadId is :{2} Callback RequestId : {0} 回调结束", callbackRequestId, DateTime.Now, Thread.CurrentThread.ManagedThreadId));
Console.WriteLine("==================");
Console.WriteLine();
}
#endregion
}
{
InstanceContext instanceContext=new InstanceContext(new AddCallback());
DuplexChannelFactory<IAdd> factory = new DuplexChannelFactory<IAdd>(instanceContext,"AddService");
for (int i = 0; i < 2; i++)
{
IAdd proxy = factory.CreateChannel();
ThreadPool.QueueUserWorkItem(delegate
{
int clientId = Interlocked.Increment(ref index);
using (
OperationContextScope contextScope =
new OperationContextScope(proxy as IContextChannel))
{
MessageHeader<int> header = new MessageHeader<int>(clientId);
System.ServiceModel.Channels.MessageHeader messageHeader =
header.GetUntypedHeader(MessageWrapper.headerClientId,
MessageWrapper.headerNamespace);
OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader);
proxy.Add(1, 2);
}
});
}
}
<wsDualHttpBinding>
<bind name="wsDuplexBinding">
<clientBaseAddress address="http://127.0.0.1:6300/addCallbackService">
<bind>
<wsDualHttpBinding>
</bindings>
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single


测试2:
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall
服务端输出:

服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
客户端输出:
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
客户端输出:
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
客户端:ConcurrencyMode = ConcurrencyMode.Single

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerSession
客户端输出:
ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall
客户端输出:

ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.Single
服务端输出:
客户端输出:
服务端:ConcurrencyMode = ConcurrencyMode.Reentrant,InstanceContextMode = InstanceContextMode.PerCall
客户端:ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple,InstanceContextMode = InstanceContextMode.PerCall)
服务端输出:
客户端输出:
由测试10、11、12、18这些测试得出如下结论:ServiceBehaviorAttribute对回调服务的并发设置无效。
会话模式下,不同客户端代理对可重入的服务总是以并发的方式执行;相同客户端对可重入的服务总是以串行的方式执行,而无论怎样,回调服务总是以串行的方式执行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构