6

Webページのコンテンツをダウンロードする非同期タスク(.NET 4.5上)を開始しようとしていますが、どういうわけかこのタスクは終了しません。

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

}
}

呼び出してページのダウンロードを開始します

new PageDownloader().GetPageData(url);

コードをデバッグしようとしているときは、GetResponse().Wait(). しかし、どういうわけか GetResponse()タスクが終了しません-次の行のブレークポイントに到達しません。例外は発生しません。アプリケーションは引き続き実行されます。助言がありますか?

4

2 に答える 2

10

asyncこれは、操作を開始し、返されたタスクをブロックしたときに発生する標準的なデッドロック状態です。

これは、トピックに関するブログ投稿のディスカッションです。

基本的に、await呼び出しは、タスクの接続された継続が最初にあったコンテキストで実行されることを保証します (これは非常に役立ちます) がWait、同じコンテキストで呼び出しているため、ブロックされているため、継続は決して実行されず、その継続待機が終了するまで実行する必要があります。古典的なデッドロック。

修正に関しては; 通常、非同期操作でブロッキング待機を行うべきではないことを意味します。それはシステム全体の設計に反しています。「完全に非同期」にする必要があります。この場合、 は ではなく を返す必要があり、タスクを返す他の操作を待機するのではなく、それらに対して実行する必要があることを意味GetPageDataします。Task<string>stringawait

そうは言っても、デッドロックせずに非同期操作でブロッキング待機を行う方法があります。それは可能ですが、そもそも async/await を使用する目的に正直に反します。このシステムを使用する主な利点は、メイン コンテキストがブロックされないことです。それをブロックすると、その利点全体がなくなり、ブロックコードをずっと使用することもできます。 async/awaitは、実際にはオール オア ナッシングのパラダイムです。

そのクラスをどのように構造化するかを次に示します。

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(); ;
    }
}
于 2012-11-09T19:10:57.260 に答える
1

そのような機能が必要な場合は、これを行ってみませんか。

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

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);
}
于 2012-11-09T19:19:49.187 に答える