如何修复具有SynchronizationContext的线程池线程的死锁?

CSh*_*ark 6 .net c# synchronizationcontext task-parallel-library async-await

我在使用HttpClient发送http请求时遇到间歇性死锁,有时它们永远不会返回到await SendAsync我的代码中.我能够找出处理内部请求的线程HttpClient/ HttpClientHandler由于某种原因SynchronizationContext它在死锁期间有一个.我想弄清楚如何使用线程最终得到a SynchronizationContext,通常他们没有.我会假设无论什么对象导致SynchronizationContext设置它也会阻塞Thread,这会导致死锁.

我是否能够在TPL ETW事件中看到任何相关内容?

我该如何解决这个问题?



编辑2: 我注意到这些死锁的地方是在ServiceContractwindows服务中的wcf (参见下面的代码)中.该SynchronizationContext所导致的问题实际上是一个是WindowsFormsSynchronizationContext,我以为是一些控制入门创建并造成没有清理正确(或者类似).我意识到几乎可以肯定不应该在Windows服务中发生任何Windows窗体的事情,我并不是说我同意它是如何使用的.但是,我没有使用它编写任何代码,我不能轻易地改变所有的引用.

编辑:这是我遇到问题的wcf服务的一般概念的一个例子.这是一个简化版本,而不是确切的代码:

[ServiceContract]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
internal class SampleWcfService
{
    private readonly HttpMessageInvoker _invoker;

    public SampleWcfService(HttpMessageInvoker invoker)
    {
        _invoker = invoker;
    }

    [WebGet(UriTemplate = "*")]
    [OperationContract(AsyncPattern = true)]
    public async Task<Message> GetAsync()
    {
        var context = WebOperationContext.Current;
        using (var request = CreateNewRequestFromContext(context))
        {
            var response = await _invoker.SendAsync(request, CancellationToken.None).ConfigureAwait(false);
            var stream = response.Content != null ? await response.Content.ReadAsStreamAsync().ConfigureAwait(false) : null;
            return StreamMessageHelper.CreateMessage(MessageVersion.None, "GETRESPONSE", stream ?? new MemoryStream());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

添加ConfigureAwait(false)到上面的2个地方并没有完全解决我的问题,因为用于服务进入此处的wcf请求的线程池线程可能已经有了SynchronizationContext.在这种情况下,请求会一直通过整个GetAsync方法并返回.然而,它仍然最终陷入僵局System.ServiceModel.Dispatcher.TaskMethodInvoker,因为在那个微软代码中,它没有使用ConfigureAwait(false),我想假设有一个很好的理由(供参考):

var returnValueTask = returnValue as Task;

if (returnValueTask != null)
{
    // Only return once the task has completed                        
    await returnValueTask;
}
Run Code Online (Sandbox Code Playgroud)

感觉真的错了,但是将它转换为使用APM(开始/结束)而不是使用任务修复此问题?或者,唯一的解决方法是只更正未SynchronizationContext正确清理的代码?

Jon*_*Boy 2

尝试下面;我在类似的情况下成功进入了异步兔子洞。

var responsebytes = await response.Content.ReadAsByteArrayAsync();
MemoryStream stream = new MemoryStream(filebytes);
Run Code Online (Sandbox Code Playgroud)

响应流变量。

希望能帮助到你。