使用async / await调用GetAsync / SendAsync的Singleton HttpClient永远不会返回

时间:2015-09-18 16:22:27

标签: c# .net async-await xamarin.android dotnet-httpclient

我正在使用Xamarin(Android)构建应用程序,它使用PCL项目作为服务层。我有一个Web Api端点,我使用HttpClient来使用它。

一切正常,但是如果我让我的Android应用程序打开并闲置一段时间(比如2分钟)并且我尝试发出新请求,那么使用单例HttpClient的第一个请求将无效。它永远不会返回并保持在那里直到超时(TaskCancelledException)。我也在我的Api上设置了一个断点,它没有被击中。如果我再次尝试发送请求,那么它可以正常工作。

经过大量调试后,我发现只有当我尝试将HttpClient用作Singleton时才会发生这种情况。如果我为每个请求创建一个新的HttpClient,一切正常。

起初我认为这是一个死锁问题,我做了很多研究,并按照other answerStephen Cleary's excellent post中描述的指导原则仔细检查了一切,我几乎可以肯定这是不是这样的。
我在PCL项目的每个调用中使用ConfigureAwait(false),因此它不捕获上下文。

请求流程如下:

在Android片段中:

SampleService svc = new SampleService();
response = await svc.GetAllSamples();

该服务(在我的PCL项目中):

public class SampleService
{
    public HttpClient Client { get; set; }

    public SampleService()
    {
        // resolves my singleton instance and uses my custom DelegatingHandler
        Client = CustomHttpClient.Instance;
    }

    public async Task<IEnumerable<Sample>> GetAllSamples()
    {
        IEnumerable<Sample> list = null;

        // this never returns and timeouts the first time
        using (var response = await Client.GetAsync("samples").ConfigureAwait(false))
        {
            if (response.IsSuccessStatusCode)
            {
                string json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                lista = await Task.Run(() => JsonConvert.DeserializeObject<IEnumerable<Sample>>(json)).ConfigureAwait(false);
            }

            return list;
        }
    }
}

这就是我构建Singleton实例的方法:

public sealed class CustomHttpClient
{
    private static HttpClient _client;

    public static HttpClient GetClient()
    {
        if (_client == null)
        {
            HttpMessageHandler messageHandler = new HttpClientHandler();
            _client = new HttpClient(messageHandler);
            _client.Timeout = TimeSpan.FromSeconds(30);
            _client.BaseAddress = new Uri("myendpoint");
            _client.DefaultRequestHeaders.Accept.Clear();
            _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        }

        return _client;
    }
}

我试图在这里简化和隔离代码,如果我可以提供任何其他有用的代码段,请告诉我。

我是否对单身HttpClient做了一些我不知道的事情?

更新:为了澄清,我正在尝试将HttpClient用作单身人士,正如我在this answer by Darrel Miller和书中{{3}中找到的那样(第14章),它们被设计为可重用且线程安全(在大多数情况下)。根据我的研究,我没有使用任何非线程安全的东西。

0 个答案:

没有答案