简体   繁体   English

在控制台应用程序中等待异步操作后,WCF 客户端挂起任何操作

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM