我正在做一些 WCF 实验,以了解我可以使用会话、回调和 TPL 运行多远。其中一个实验产生了奇怪的结果......服务是通过双向绑定 (NetTcpBinding) 实现的,并且通知是作为回调实现的。预期的行为应该是:
- 客户端以同步方式调用服务(Do)上的方法
- 服务以同步方式在客户端调用回调
- 客户端(在回调中)以异步方式调用服务上的第二个 a 方法
该服务需要一个会话,InstanceContext 是 PerSession,并发模式是 Single。在这种情况下,我知道,在回调的上下文中,我不能以同步方式调用服务的任何方法(在同一客户端上),因此代码可以是:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(INotificationCallback))]
public interface INotificationService
{
[OperationContract]
int Do(int value);
[OperationContract]
int ReDo(int value);
}
public interface INotificationCallback
{
[OperationContract(IsOneWay = true)]
void Notify(int value);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
public class NotificationService : INotificationService
{
public int Do(int value)
{
var notificationCallback = OperationContext.Current.GetCallbackChannel<INotificationCallback>();
//Task.Run(() => notificationCallback.Notify(value * 2));
notificationCallback.Notify(value * 2);
Thread.Sleep(1000);
return value * 2;
}
public int ReDo(int value)
{
return value + 1;
}
}
客户端回调接口的实现:
class CallbackInstance : INotificationServiceCallback
{
public NotificationServiceClient Client { get; set; }
#region Implementation of INotificationServiceCallback
public void Notify(int value)
{
Console.WriteLine("Notified: {0}", value);
Task<int> task = Client.ReDoAsync(value);
task.ContinueWith(t => Console.WriteLine("In Notify - ReDo({0}): {1}", value, t.Result));
}
#endregion
}
运行所有代码的主要方法:
static void Main(string[] args)
{
CallbackInstance callback = new CallbackInstance();
NotificationServiceClient client = new NotificationServiceClient(new InstanceContext(callback));
callback.Client = client;
int result = client.Do(10);
Console.WriteLine("Do({0}): {1}", 10, result);
Console.ReadLine();
}
在我的想法中,Notify 方法的实现中的行:
Task<int> task = Client.ReDoAsync(value);
task.ContinueWith(t => Console.WriteLine("In Notify - ReDo({0}): {1}", value, t.Result));
应该打破死锁但是......不,当调用 ReDoAsync 时会抛出一个异常,说“这个操作会死锁,因为在当前消息完成处理之前无法接收到回复......”。是的,亲爱的,我知道。事实上,我想以异步方式回调服务以绕过问题,但它不起作用。
但让我抓狂的是,稍微修改一下代码就能让它像魅力一样工作。而不是我尝试的 ReDoAsync 调用:
Task<int> task = Task.Run(() => Client.ReDo(value));
它有效!所以问题是:客户端方法的异步版本不应该进行与有效的调用相同或相似的调用吗?如果不是,那么客户端方法的异步版本到底是做什么的?
谢谢。