[英]WCF client hangs on any operation after awaiting async operation in Console Application
Client example code:客户端示例代码:
var f = new DuplexChannelFactory<IService>(new Callback(), "NetTcpBinding_Name");
f.Credentials.ClientCertificate.Certificate = new X509Certificate2(certificateFile, "", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable);
f.Open();
IService s = f.CreateChannel();
Console.WriteLine("Lock status: " + s.IsUserLocked(id));
Task task = s.LockUserAsync(id);
Console.Write("Sent, awaiting ");
Console.WriteLine(task);
await task;
Console.WriteLine("Done");
Console.WriteLine("Lock status: " + s.IsUserLocked(id)); // never returns, timeout
After awaiting on an async method LockUserAsync
any sync methods hang forever.等待异步方法
LockUserAsync
任何同步方法都将永远挂起。 The debugger shows that IsUserLocked
is actually called second time and reached its return.调试器显示
IsUserLocked
实际上被第二次调用并返回。
It doesn't affect other clients: they can connect and repeat the same thing from start.它不会影响其他客户端:他们可以从一开始就连接并重复相同的事情。
Behavior:行为:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
Contract:合同:
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
public interface IService
{
[OperationContract]
Task LockUserAsync(int userId);
[OperationContract]
int IsUserLocked(int id);
}
public interface IServiceCallback
{
[OperationContract]
void TestCallback(); // not used
}
Both methods are just placeholders:这两种方法都只是占位符:
public async Task LockUserAsync(int id)
{
return;
}
public int IsUserLocked(int id)
{
return 0;
}
Update: it's a console application so no synchronization context.更新:它是一个控制台应用程序,所以没有同步上下文。 If I replace
await
with .Wait()
it works.如果我用
.Wait()
替换await
它会起作用。 ConfigureAwait(false)
doesn't change anything. ConfigureAwait(false)
不会改变任何东西。
I've found that the continuation is somehow directly called from Task.SetResult
.我发现延续以某种方式直接从
Task.SetResult
。 Why it's not invoked through ThreadPool
?为什么不通过
ThreadPool
调用它?
When there is no SynchronizationContext
like with ConsoleApplication TPL tries to optimize things by invoking continuations directly from Task.TrySetResult
.当没有像 ConsoleApplication 这样的
SynchronizationContext
,TPL 尝试通过直接从Task.TrySetResult
调用延续来优化事物。 Since my code calls WCF back from continuation it causes a deadlock inside external WCF code.由于我的代码从延续中调用 WCF,它会导致外部 WCF 代码内部出现死锁。
The solution was to put await Task.Yield();
解决方案是放置
await Task.Yield();
after each await s.WcfMethod();
每次
await s.WcfMethod();
which causes next continuation to be invoked from ThreadPool
.这会导致从
ThreadPool
调用下一个延续。
Personally I think that it's a bug of WCF: it should call Task.SetResult
inside Task.Run()
or pass TaskCreationOptions.RunContinuationsAsynchronously to TaskCompletionSource
settings.我个人认为这是 WCF 的一个错误:它应该在
Task.Run()
调用Task.SetResult
或将TaskCreationOptions.RunContinuationsAsynchronously传递给TaskCompletionSource
设置。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.