好的,我的机智已经结束了这件事。我有一个WCF双工服务。以下是架构的工作原理:
问题是当服务调用回调时,似乎没有任何反应。没有错误,没有任何东西。经过进一步调查后,我在服务器跟踪中发现了一个异常:
The I/O operation has been aborted because of either a thread exit or an application request
这在尝试执行回调后立即发生。
客户端永远不会收到回复或关闭。发生的一切都是,因为初始请求是在与主要线程不同的线程上进行的,所以它只是永远等待那个线程完成。
最奇怪的是,如果我尝试在客户端调用的操作中调用回调,而不进入另一个线程,一切正常 - 回调被成功调用,这让我相信我已经配置服务正确,但有线程/死锁问题。
以下是我如何调用该服务:
SubmissionServiceClient client = CreateClientInstance();
client.Open();
Guid executionId = await client.SubmitAsync(submission);
submissionCompletionSource.Task.Wait(); //waits for the callback to be called (I omitted the extra wiring code for better readability)
client.Close();
private SubmissionServiceClient CreateClientInstance()
{
NetHttpBinding binding = new NetHttpBinding();
binding.WebSocketSettings.TransportUsage = WebSocketTransportUsage.Always;
EndpointAddress endpointAddress = new EndpointAddress("ws://localhost:9080/SubmissionRouter");
InstanceContext instanceContext = new InstanceContext(this);
SubmissionServiceClient submissionServiceClient = new SubmissionServiceClient(instanceContext,binding,endpointAddress);
return submissionServiceClient;
}
这是回调操作:
public void SubmissionProcessed(SubmissionResultDto result)
{
submissionCompletionSource.TrySetResult(result);
}
这是客户端调用的服务操作:
public Guid Submit(SubmissionDto submission, ISubmissionCallback callback)
{
ExecutionDto execution = new ExecutionDto()
{
Id = Guid.NewGuid(),
Submission = submission
};
RequestExecution(execution); //Queues the execution of the operation
submissions.Add(execution.Id, callback);
return execution.Id;
}
这是服务调用客户端回调的地方(此方法在与初始请求的线程不同的线程上执行):
ISubmissionCallback callback = submissions[submissionResult.ExecutionId];
callback.SubmissionProcessed(submissionResult);
服务行为:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
正如您在提交时看到的那样,该服务将回调存储在字典中,并与id配对,稍后用它来检索回调并调用它。我相信它失败的原因是因为我试图在另一个线程上这样做。
修改 我在服务中添加了一个Ping操作,该操作调用另一个线程,该线程挂起3秒钟,然后在客户端上调用Pong函数。
public void Ping()
{
var callback = OperationContext.Current.GetCallbackChannel<ISubmissionCallback>();
Task.Run(() =>
{
System.Threading.Thread.Sleep(3000);
callback.Pong();
});
}
客户端: 课程 { static void Main(string [] args) { NetHttpBinding binding = new NetHttpBinding(); binding.WebSocketSettings.TransportUsage = WebSocketTransportUsage.Always; EndpointAddress endpointAddress = new EndpointAddress(“ws:// localhost:9080 / SubmissionRouter”); InstanceContext instanceContext = new InstanceContext(new Callback()); SubmissionServiceClient submissionServiceClient = new SubmissionServiceClient(instanceContext,binding,endpointAddress);
submissionServiceClient.Ping();
Console.Read();
}
public void SubmissionProcessed(SubmissionResultDto result)
{
throw new NotImplementedException();
}
class Callback : ISubmissionServiceCallback
{
public void Pong()
{
Console.WriteLine("Pong!");
}
public void SubmissionProcessed(SubmissionResultDto result)
{
}
}
}
这实际上成功了。我设法在客户端收到我的答复。我现在正式完全迷失了。
答案 0 :(得分:3)
如果您阻止导致死锁的submissionCompletionSource.Task.Wait();
的UI线程。默认情况下,WCF回调发生在UI线程上,您可以使用CallbackBehaviorAttribute
[CallbackBehaviorAttribute(UseSynchronizationContext=false)]
class Callback : ISubmissionServiceCallback
{
public void Pong()
{
Console.WriteLine("Pong!");
}
public void SubmissionProcessed(SubmissionResultDto result)
{
}
}
或者不阻止UI线程。
SubmissionServiceClient client = CreateClientInstance();
client.Open();
Guid executionId = await client.SubmitAsync(submission);
await submissionCompletionSource.Task; //awaits for the callback to be called without blocking the UI.
client.Close();