我遇到了一个我不太理解的问题。
该问题与客户端线程之间的同步有关,但我找不到其根本原因。我创建了一个小演示来模拟这个问题,所以它会更容易解释。
服务联系人:
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(ITestServiceCallback))]
public interface ITestService
{
[OperationContract]
Task RegisterAsync();
[OperationContract]
Task UnRegisterAsync();
[OperationContract]
int Test1(int i);
}
回调合约:
public interface ITestServiceCallback
{
[OperationContract(IsOneWay = true)]
void TestCallback(int value);
}
服务实施:
调用Test1
方法时的服务将使用提供给方法的值引发回调Test1
。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class TestService : ITestService
{
private readonly HashSet<ITestServiceCallback> _callbacks = new HashSet<ITestServiceCallback>();
public Task RegisterAsync()
{
Console.WriteLine("Registering Callback, Thread: {0}", Thread.CurrentThread.ManagedThreadId);
var callbackProxy = OperationContext.Current.GetCallbackChannel<ITestServiceCallback>();
_callbacks.Add(callbackProxy);
return Task.CompletedTask;
}
public Task UnRegisterAsync()
{
Console.WriteLine("Unregistering Callback, Thread: {0}", Thread.CurrentThread.ManagedThreadId);
var callbackProxy = OperationContext.Current.GetCallbackChannel<ITestServiceCallback>();
bool removed = _callbacks.Remove(callbackProxy);
Console.WriteLine("Callback was{0} successfully removed", removed ? "" : " not");
return Task.CompletedTask;
}
public int Test1(int i)
{
Console.WriteLine("Working on server, Test1, Thread: {0}", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(500);
raiseCallback(i);
Console.WriteLine("Finished on server, Test1, Thread: {0}", Thread.CurrentThread.ManagedThreadId);
return i;
}
private void raiseCallback(int value)
{
foreach (var callback in _callbacks)
{
try
{
callback.TestCallback(value);
}
catch (Exception ex)
{
Console.WriteLine("Callback calling failed in server: {0}", ex);
}
}
}
}
客户代码:
客户端首先注册到服务器的回调(注意异步注册调用 -这就是问题)然后调用Test1
. 这里的调用Test1
永远不会返回
var callBack = new ClientCallback();
var client = DuplexChannelFactory<ITestService>.CreateChannel(callBack, _defaultBinding, _defaultEndpointAddress);
using (client as IDisposable)
{
await client.RegisterAsync(); // client.RegisterAsync.Wait();
Console.WriteLine("Client Thread: {0}", Thread.CurrentThread.ManagedThreadId);
try
{
int res = client.Test1(5);
Console.WriteLine("Test1 was called, Server Result: {0}", res);
}
finally
{
await client.UnRegisterAsync();
}
}
[CallbackBehavior(UseSynchronizationContext = false)]
class ClientCallback : ITestServiceCallback
{
public void TestCallback(int value)
{
Console.WriteLine("Callback Called, Value: {0}, Thread: {1}", value, Thread.CurrentThread.ManagedThreadId);
}
}
RegisterAsync
同步调用(使用.Wait()
)时,调用Test1
不会死锁。- 在服务器中调用
Test1
通行证没有任何问题。 使用的绑定是 NetNamedPipeBinding:
绑定_defaultBinding = new NetNamedPipeBinding { MaxBufferSize = int.MaxValue, MaxReceivedMessageSize = int.MaxValue, ReceiveTimeout = TimeSpan.MaxValue, SendTimeout = TimeSpan.MaxValue, CloseTimeout = TimeSpan.MaxValue };
我认为将UseSynchronizationContext
属性设置为false
(在 theServiceBehaviourAttribute
和 in 中CallbackBehaviourAttribute
)应该可以解决这个问题。