简体   繁体   English

异步任务没有结束

[英]Async task does not end

I'm trying to start async task (on .NET 4.5) which downloads content of web page, but somehow this task never finishes. 我正在尝试启动异步任务(在.NET 4.5上)下载网页内容,但不知怎的,这个任务永远不会完成。

My PageDownloader class: 我的PageDownloader类:

using System.Net;
using System.Text;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using System;

namespace ParserConsole.WebClient
{
public class PageDownloader
{
    private System.Net.Http.HttpClient _client;

    public PageDownloader()
        : this(Encoding.UTF8) { }

    private Encoding _encoding;

    public PageDownloader(Encoding encoding)
    {
        _encoding = encoding;
        _client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10)};
    }

    private HttpRequestMessage _request;
    private HttpResponseMessage _response;
    private string _responseString;

    public string GetPageData(string link)
    {
        _request = new HttpRequestMessage(HttpMethod.Get, link);
        _request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
        _request.Headers.Add("Accept", "text/html");


        GetResponse().Wait();
        GetStringFromResponse().Wait();
        return _responseString;            
    }

    private async Task<HttpResponseMessage> GetResponse() {
        return _response = await _client.GetAsync(_request.RequestUri);
    }

    private async Task<string> GetStringFromResponse() {
        return _responseString = await _response.Content.ReadAsStringAsync();
    }

}
}

I start downloading page by calling 我开始通过电话下载页面

new PageDownloader().GetPageData(url);

When I'm trying to debug the code, everything is fine till GetResponse().Wait() . 当我尝试调试代码时,一切都很好,直到GetResponse().Wait() But somehow GetResponse() task never finishes - breakpoint on the next line is never reached. 但不知何故, GetResponse()任务永远不会完成 - 永远不会到达下一行的断点。 I get no exceptions, application continues running. 我没有例外,应用程序继续运行。 Any suggestions? 有什么建议?

This is a standard deadlock condition you get when you start an async operation and then block on the returned task. 这是您在启动async操作然后阻止返回的任务时获得的标准死锁条件。

Here is a blog post discussion the topic. 是一篇关于该主题的博客文章讨论。

Basically, the await call ensures that the continuation it wires up of the task will run in the context you were originally in (which is very helpful) but because you are calling Wait in that same context it's blocking, so the continuation never runs, and that continuation needs to run for the wait to end. 基本上, await调用确保它连接任务的延续将在你最初的上下文中运行(这是非常有帮助的)但是因为你在同一个上下文中调用Wait它是阻塞的,所以延续从不运行,并且延续需要运行等待结束。 Classic deadlock. 经典的僵局。

As for the fix; 至于修复; usually it means you just shouldn't be doing a blocking wait on the async operation; 通常它意味着你不应该在异步操作上做阻塞等待; it's contrary to the design of the whole system. 这与整个系统的设计相反。 You should, "async all the way up". 你应该“一直异步”。 In this case it would mean that GetPageData should return a Task<string> rather than a string , and rather than waiting on the other operations that return a task you should await on them. 在这种情况下,它意味着GetPageData应该返回一个Task<string>而不是一个string ,而不是等待返回任务的其他操作,你应该await它们。

Now, having said that, there are ways of doing a blocking wait on the async operations without deadlocking. 现在,已经说过,有一些方法可以在没有死锁的情况下对异步操作进行阻塞等待。 While it can be done, it honestly defeats the purpose of using async/await in the first place. 虽然可以做到,但它首先实际上违背了使用async / await的目的。 The primary advantage of using that system is that the main context isn't blocked; 使用该系统的主要优点是主要上下文不被阻止; when you block on it that entire advantage goes away, and you might as well just use blocking code all the way through. 当你阻止整个优势消失时,你也可以一直使用阻塞代码。 async / await is really more of an all-or-nothing paradigm. async / await实际上更像是一个全有或全无的范例。

Here is how I would structure that class: 以下是我将如何构建该类:

public class PageDownloader
{
    private System.Net.Http.HttpClient _client;
    private Encoding _encoding;

    public PageDownloader()
        : this(Encoding.UTF8) { }

    public PageDownloader(Encoding encoding)
    {
        _encoding = encoding;
        _client = new HttpClient() { Timeout = TimeSpan.FromSeconds(10) };
    }

    public async Task<string> GetPageData(string link)
    {
        HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, link);
        request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
        request.Headers.Add("Accept", "text/html");

        HttpResponseMessage response = await _client.GetAsync(request.RequestUri);

        return await response.Content.ReadAsStringAsync(); ;
    }
}

Why not just do this if you want to have a function like that. 如果你想拥有这样的功能,为什么不这样做呢?

public string GetPageData(string link)
{
    _request = new HttpRequestMessage(HttpMethod.Get, link);
    _request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
    _request.Headers.Add("Accept", "text/html");


    var readTask = _client.GetStringAsync(link);
    readTask.Wait();
    return readTask.Result;
}

It would be better to return the Task all the way back and handle it with async/await in the calling code. 最好将Task一直返回并在调用代码中使用async / await处理它。

public Task<string> GetPageData(string link)
{
    _request = new HttpRequestMessage(HttpMethod.Get, link);
    _request.Headers.Add("User-Agent", "Chrome/21.0.1180.89");
    _request.Headers.Add("Accept", "text/html");


    return _client.GetStringAsync(link);
}

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

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