从不同线程调用时,WCF Duplex 回调方法从不执行
WCF Duplex callback method never executes when invoked from a different thread
好吧,我对这件事束手无策。我有一个 WCF 双工服务。以下是架构的工作原理:
- 客户端打开到端点的连接并提供回调实现
- 该服务接受该请求并在其他线程上做一些事情(可能是 1 秒也可能是 2 分钟,这就是我不使用异步操作的原因)
- 处理完成后调用客户端的回调
问题是当服务调用那个回调时,似乎没有任何反应。没有错误,什么都没有。经过进一步调查,我在服务器跟踪中发现了一个异常:
The I/O operation has been aborted because of either a thread exit or an application request
这发生在尝试执行回调之后。
客户端从未收到响应或关闭。所发生的一切是,由于初始请求是在与主线程不同的线程上发出的,它只是永远等待该线程完成。
最奇怪的是,如果我尝试在客户端调用的操作中调用回调,而不进入另一个线程,一切正常 - 回调被成功调用,这让我相信我已正确配置服务,但遇到 threading/deadlocking 问题。
我是这样调用服务的:
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();
});
}
客户:
class 程序
{
静态无效主要(字符串[]参数)
{
NetHttpBinding 绑定 = 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)
{
}
}
}
这实际上成功了。我设法在客户端收到了我的答复。我现在正式完全迷路了。
如果您使用 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();
好吧,我对这件事束手无策。我有一个 WCF 双工服务。以下是架构的工作原理:
- 客户端打开到端点的连接并提供回调实现
- 该服务接受该请求并在其他线程上做一些事情(可能是 1 秒也可能是 2 分钟,这就是我不使用异步操作的原因)
- 处理完成后调用客户端的回调
问题是当服务调用那个回调时,似乎没有任何反应。没有错误,什么都没有。经过进一步调查,我在服务器跟踪中发现了一个异常:
The I/O operation has been aborted because of either a thread exit or an application request
这发生在尝试执行回调之后。
客户端从未收到响应或关闭。所发生的一切是,由于初始请求是在与主线程不同的线程上发出的,它只是永远等待该线程完成。
最奇怪的是,如果我尝试在客户端调用的操作中调用回调,而不进入另一个线程,一切正常 - 回调被成功调用,这让我相信我已正确配置服务,但遇到 threading/deadlocking 问题。
我是这样调用服务的:
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();
});
}
客户: class 程序 { 静态无效主要(字符串[]参数) { NetHttpBinding 绑定 = 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)
{
}
}
}
这实际上成功了。我设法在客户端收到了我的答复。我现在正式完全迷路了。
如果您使用 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();