繁体   English   中英

不等待HttpClient使用时会发生什么

[英]What happens when HttpClient usages are not awaited

给定的代码类似于

Task.Run(() =>
{
    using (var client = new HttpClient())
    {
        var responseTask = client.GetAsync(urlToInvoke);
    }
});

在这种情况下, GetAsync似乎实际上没有运行。 是在完成之前取消任务还是在这里实际发生了什么?

现在,如果您稍作更改并插入

Task.Run(() =>
{
    using (var client = new HttpClient())
    {
        var responseTask = client.GetAsync(urlToInvoke);

        Task.Delay(5000).Wait()
    }
});

GetAsync不会完全执行。 这里发生了什么? Task.Delay affinitizing本身相同的任务就是里面responseTask最终使这相当于responseTask.Wait()

当您不await (或Wait )任务时,它们不会自动取消。 它们继续运行,直到达到以下三种状态之一:

  • RanToCompletion-成功完成。
  • 已取消-取消令牌已取消。
  • 有故障-任务内部发生未处理的异常。

但是,在您的情况下,由于没有人等待任务完成,因此使用范围结束了对HttpClient配置。 在这种情况下,这将取消所有客户端的任务client.GetAsync(urlToInvoke) 这样内部async任务将立即结束并被取消,而外部任务( Task.Run )将不执行任何操作而直接结束。

当您使用Task.Delay(5000).Wait()基本上是Thread.Sleep(5000) ,任务有机会在使用范围结束之前完成。 但是,应避免这种操作方式。 它会在整个Wait阻塞线程,并可能导致单线程SynchronizationContext的死锁。 这也隐藏了任务中可能出现的异常(这可能会破坏早期版本的.Net中的应用程序)

您应该始终等待任务完成,最好是异步完成,正如Servy所说,没有理由使用Task.Run在这里进行卸载,因为GetAsync是异步的,不会阻塞调用线程。

using (var client = new HttpClient())
{
    var response = await client.GetAsync(urlToInvoke);
}

您正在错误地考虑它。 这是类内部正在发生的事情的伪版本。

class HttpClient : IDisposeable
{
    private CancelationTokenSource _disposeCts;

    public HttpClient()
    {
        _disposeCts = new CancelationTokenSource();
    }

    public Task<HttpResponseMessage> GetAsync(string url)
    {
        return GetAsync(url, CancellationToken.None);
    }

    public async Task<HttpResponseMessage> GetAsync(string url, CancelationToken token)
    {
        var combinedCts =
            CancellationTokenSource.CreateLinkedTokenSource(token, _disposeCts.Token);
        var tokenToUse = combinedCts.Token;

        //... snipped code

        //Some spot where it would good to check if we have canceled yet.
        tokenToUse.ThrowIfCancellationRequested();

        //... More snipped code;

        return result;
    }

    public void Dispose()
    {
        _disposeCts.Cancel();
    }

    //... A whole bunch of other stuff.
}

重要的是要看到当您退出using块时,内部的取消令牌被取消了。

在你的第一个例子中的任务没有完成却又如此tokenToUse现在会抛出如果ThrowIfCancellationRequested()被调用。

在您的第二个示例中,任务已经完成,因此取消内部令牌的操作对由于已达到完成状态而返回的任务没有影响。

这就好比问为什么这导致任务被取消。

using (var client = new HttpClient())
{
    var cts = new CancellationTokenSource()
    var responseTask = client.GetAsync(urlToInvoke, cts.Token);

    cts.Cancel();
}

但这不是

using (var client = new HttpClient())
{
    var cts = new CancellationTokenSource()
    var responseTask = client.GetAsync(urlToInvoke, cts.Token);

    Task.Delay(5000).Wait()
    cts.Cancel();
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM