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

时间:2018-03-16 00:09:04

标签: c# .net async-await task-parallel-library synchronizationcontext

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

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

我该如何解决这个问题?



编辑2: 我注意到这些死锁的地方是在windows服务中的wcf ServiceContract(参见下面的代码)中。导致问题的SynchronizationContext实际上是WindowsFormsSynchronizationContext,我认为这是由某些控件创建并且未正确清理(或类似的东西)引起的。我意识到几乎肯定不应该是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());
        }
    }
}

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

var returnValueTask = returnValue as Task;

if (returnValueTask != null)
{
    // Only return once the task has completed                        
    await returnValueTask;
}

感觉真的错了,但是将它转换为使用APM(开始/结束)而不是使用任务修复此问题?或者,是唯一能够正确修正未正确清除其SynchronizationContext的代码的修复程序吗?

1 个答案:

答案 0 :(得分:2)

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

var responsebytes = await response.Content.ReadAsByteArrayAsync();
MemoryStream stream = new MemoryStream(filebytes);

响应流变量。

希望它有所帮助。