首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HttpClient等待SendAsync死锁

HttpClient等待SendAsync死锁
EN

Stack Overflow用户
提问于 2017-05-18 14:11:41
回答 1查看 1.4K关注 0票数 3

我已经和这件事斗争了好几天了,我不知道为什么它是这样的。我希望你们中的一个能给我解释一下。

以下是有关的守则:

代码语言:javascript
复制
public async static Task<RemoteFileDetails> GetRemoteFileDetailsAsync(string strUrl, double dblTimeoutSeconds = 15.0)
{
    try
    {
        using (var hc = new HttpClient())
        {
            hc.Timeout = TimeSpan.FromSeconds(dblTimeoutSeconds);
            using (var h = new HttpRequestMessage(HttpMethod.Head, strUrl))
            {
                h.Headers.UserAgent.ParseAdd(UserAgent);
                using (var rm = await hc.SendAsync(h))
                {
                    rm.EnsureSuccessStatusCode();
                    return new RemoteFileDetails()
                    {
                        Url = strUrl,
                        FileName = rm.Content.Headers.ContentDisposition.FileName,
                        FileSize = rm.Content.Headers.ContentLength.GetValueOrDefault(),
                        LastModified = rm.Content.Headers.LastModified.GetValueOrDefault().LocalDateTime,
                        Valid = true
                    };
                }
            }
        }
    }
    catch (Exception ex)
    {
        System.Windows.MessageBox.Show(ex.ToString());
    }
    return new RemoteFileDetails();
}

所做的就是在URL上输入一个HEAD请求并获取特定的标头结果。

这里的问题是大约50%的时间它死锁(暂停60秒左右,然后抛出一个TaskCanceledException),其余的50%,它的工作出色。现在,我添加了网络日志记录,这就是死锁发生的情况:

代码语言:javascript
复制
    System.Net Verbose: 0 : [2928] HttpWebRequest#3567399::HttpWebRequest(https://www.example.com/file.txt#-114720138)
    System.Net Information: 0 : [2928] Current OS installation type is 'Client'.
    System.Net Information: 0 : [2928] RAS supported: True
    System.Net Verbose: 0 : [2928] Exiting HttpWebRequest#3567399::HttpWebRequest() 
    System.Net Verbose: 0 : [2928] HttpWebRequest#3567399::HttpWebRequest(uri: 'https://www.example.com/file.txt', connectionGroupName: '565144')
    System.Net Verbose: 0 : [2928] Exiting HttpWebRequest#3567399::HttpWebRequest() 
    System.Net Verbose: 0 : [5456] HttpWebRequest#3567399::BeginGetResponse()
    System.Net Verbose: 0 : [2244] HttpWebRequest#3567399::Abort()
    System.Net Error: 0 : [2244] Exception in HttpWebRequest#3567399:: - The request was aborted: The request was canceled..
/////// THIS IS WHERE THE 30-60 SECOND PAUSE HAPPENS ////////
    System.Net Error: 0 : [5456] Can't retrieve proxy settings for Uri 'https://www.example.com/file.txt'. Error code: 12180.
    System.Net Verbose: 0 : [5456] ServicePoint#39086322::ServicePoint(www.example.com:443)
    System.Net Information: 0 : [5456] Associating HttpWebRequest#3567399 with ServicePoint#39086322
    System.Net Verbose: 0 : [2244] Exiting HttpWebRequest#3567399::Abort() 
    System.Net Verbose: 0 : [5456] HttpWebRequest#3567399::EndGetResponse()
    System.Net Error: 0 : [5456] Exception in HttpWebRequest#3567399::EndGetResponse - The request was aborted: The request was canceled..
    System.Net Verbose: 0 : [5456] Exiting HttpWebRequest#3567399::BeginGetResponse()   -> ContextAwareResult#36181605

那么,看起来HttpClient()对象正在被释放吗?但是怎么做呢?为了验证是否存在这种情况,我将代码更改为:

代码语言:javascript
复制
private static HttpClient _hc;
public async static Task<RemoteFileDetails> GetRemoteFileDetailsAsync(string strUrl, double dblTimeoutSeconds = 15.0)
{
    _hc = new HttpClient();
    try
    {
        _hc.Timeout = TimeSpan.FromSeconds(dblTimeoutSeconds);
        using (var h = new HttpRequestMessage(HttpMethod.Head, strUrl))
        {
            h.Headers.UserAgent.ParseAdd(UserAgent);
            using (var rm = await _hc.SendAsync(h))
            {
                rm.EnsureSuccessStatusCode();
                return new RemoteFileDetails()
                {
                    Url = strUrl,
                    FileName = rm.Content.Headers.ContentDisposition.FileName,
                    FileSize = rm.Content.Headers.ContentLength.GetValueOrDefault(),
                    LastModified = rm.Content.Headers.LastModified.GetValueOrDefault().LocalDateTime,
                    Valid = true
                };
            }
        }
    }
    catch (Exception ex)
    {
        System.Windows.MessageBox.Show(ex.ToString());
    }
    return new RemoteFileDetails();
}

但它还是死死的。

为什么会发生这种事,有什么解释吗?我认为异步/等待基本上是“暂停”例程,所以无论如何,HttpClient都不应该释放。

还是我完全错过了这条船,这是另一回事。

这里是一个例外的尖叫声:

ETA:,我想展示一下我是如何称呼它的是很重要的。这是一个使用的WPF应用程序。在主视图模型中,我这样称呼它:

代码语言:javascript
复制
    public MainViewModel(IDataService ds)
    {
        _dataService = ds;
        this.Initialize();
    }

    private async void Initialize()
    {
        var det = await RemoteFileUtils.GetRemoteFileDetailsAsync("https://example.com/file.txt");
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-05-18 15:04:25

这个问题源于它是如何被调用的。

不要在调用类的构造函数中调用它。也要避免async void,除非它在事件处理程序中。

代码语言:javascript
复制
public class MainViewModel : ViewModelBase {

    public MainViewModel(IDataService ds) {
        _dataService = ds;
        this.Initialize += OnInitialize;
        //Un-comment if you want to call event immediately
        //Initialize(this, EventArgs.Empty);
    }

    public event EventHandler Initialize;

    private async void OnInitialize(object sender, EventArgs e) {
        var det = await RemoteFileUtils.GetRemoteFileDetailsAsync("https://example.com/file.txt");
    }

    //Or call this after class has created.
    public void Ready() {
        Initialize(this, EventArgs.Empty);
    }
}

接下来,不要在每次需要发出请求时创建和解析一个新的HttpClient。创建一个实例并在整个应用程序的生命周期中使用该实例。

for each distinct API that you connect to.

GetRemoteFileDetailsAsync也应该重构为一个可注入的服务,而不是静态访问。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44050189

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档