C# HttpClient等待SendAsync死锁

C# HttpClient等待SendAsync死锁,c#,http,asynchronous,async-await,C#,Http,Asynchronous,Async Await,我已经为此奋斗了好几天,但我不明白为什么会这样。我希望你们中的一位策划人能向我解释一下 以下是相关代码: public async static Task<RemoteFileDetails> GetRemoteFileDetailsAsync(string strUrl, double dblTimeoutSeconds = 15.0) { try { using (var hc = new HttpClient()) {

我已经为此奋斗了好几天,但我不明白为什么会这样。我希望你们中的一位策划人能向我解释一下

以下是相关代码:

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();
}
看来HttpClient()对象正在被处理?但是怎么做呢?为了验证是否存在这种情况,我将代码更改为:

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();
}

问题源于如何调用它

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

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
。创建一个实例,并在应用程序的整个生命周期中使用它


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

您是否尝试过
。配置等待(continueOnCapturedContext:false)
?我想知道您是否直接从UI线程调用此函数。@xxbbcc是的,它不会改变任何东西。昨天就试过了。我将更新问题以显示我如何调用它。@xxbbcc好的,我更新了问题以显示我如何调用它。@Sonic:因为调用这个。初始化();未等待,可能会导致死锁。这篇文章可能会有所帮助。我不认为这是被处置;请求正在被取消,这听起来像是一个代理配置错误。一个问题:你说每个应用程序应该只有一个HttpClient。我可以同时在多个线程之间使用HttpClient对象吗?或者我必须将HttpClient以独占方式锁定到线程,直到HTTP传输完成?@Sonic检查答案中的链接。不需要锁定客户端。还有一个问题。在上面的例子中,如果您想立即调用event,您会说
//uncomment,但如果我这样做了,我不是回到了我调用
异步void
方法的同一条船上,而没有使用wait?@Sonic调用事件处理程序,这一次允许异步void。当你有时间阅读斯蒂芬的这篇文章时Cleary@Sonic刚刚意识到我的错误。假设您引发事件,该事件将调用事件处理程序。
    public MainViewModel(IDataService ds)
    {
        _dataService = ds;
        this.Initialize();
    }

    private async void Initialize()
    {
        var det = await RemoteFileUtils.GetRemoteFileDetailsAsync("https://example.com/file.txt");
}
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);
    }
}