2012-11-09 33 views
5

Tôi đang cố bắt đầu tác vụ không đồng bộ (trên .NET 4.5) tải xuống nội dung của trang web, nhưng bằng cách nào đó tác vụ này không bao giờ kết thúc.Tác vụ không đồng bộ không kết thúc

Lớp học của tôi 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(); 
    } 

} 
} 

tôi bắt đầu trang tải bằng cách gọi

new PageDownloader().GetPageData(url); 

Khi tôi đang cố gắng để gỡ lỗi mã, mọi thứ đều tốt cho đến GetResponse().Wait(). Nhưng bằng cách nào đó, nhiệm vụ GetResponse() không bao giờ kết thúc - điểm ngắt trên dòng tiếp theo không bao giờ đạt được. Tôi không nhận được ngoại lệ, ứng dụng tiếp tục chạy. Bất kỳ đề xuất?

Trả lời

9

Đây là điều kiện bế tắc tiêu chuẩn bạn nhận được khi bạn bắt đầu hoạt động async và sau đó chặn trên tác vụ được trả lại.

Here là bài đăng trên blog thảo luận về chủ đề. Về cơ bản, cuộc gọi await về cơ bản đảm bảo rằng việc nối tiếp nó sẽ thực hiện nhiệm vụ sẽ chạy trong ngữ cảnh ban đầu của bạn (rất hữu ích) nhưng vì bạn đang gọi Wait trong cùng bối cảnh đó đang chặn, vì vậy việc tiếp tục không bao giờ chạy, và sự tiếp tục đó cần phải chạy để chờ đợi kết thúc. Cổ điển bế tắc.

Để khắc phục; thường nó có nghĩa là bạn chỉ không nên làm một sự chờ đợi chặn trên các hoạt động async; nó trái với thiết kế của toàn bộ hệ thống. Bạn nên, "async tất cả các con đường lên". Trong trường hợp này, điều đó có nghĩa là GetPageData phải trả lại Task<string> thay vì một số string và thay vì chờ các hoạt động khác trả về một tác vụ bạn nên await trên chúng.

Bây giờ, đã nói rằng, có các cách thực hiện việc chờ chặn trên các hoạt động không đồng bộ mà không có khóa bế tắc. Trong khi nó có thể được thực hiện, nó thành thật đánh bại mục đích sử dụng async/await ở nơi đầu tiên. Ưu điểm chính của việc sử dụng hệ thống đó là ngữ cảnh chính không bị chặn; khi bạn chặn nó, toàn bộ lợi thế sẽ biến mất và bạn cũng có thể sử dụng mã chặn tất cả các cách. async/await thực sự là một mô hình hoàn toàn không có gì.

Sau đây là cách tôi sẽ cấu trúc lớp rằng:

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

Cảm ơn câu trả lời hay. Bạn cũng có thể giải thích làm cách nào để xử lý phản hồi trong mã gọi điện? –

+0

@YuriyPogrebnyak Tôi đang trong quá trình chỉnh sửa. – Servy

1

Tại sao không chỉ làm điều này nếu bạn muốn có một chức năng như thế.

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

Sẽ tốt hơn để trả lại Tác vụ tất cả cách quay lại và xử lý nó bằng cách không đồng bộ/đang chờ trong mã gọi điện.

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); 
} 
+0

Làm thế nào? Tôi không bắt đầu một async/await trước cuộc gọi Wait. Tôi đang sử dụng cuộc gọi Chờ chuẩn trên Tác vụ. Có lẽ tôi đang thiếu một cái gì đó, nhưng nó trông gần như giống hệt với các ví dụ về SDK của việc sử dụng phương thức Wait. –

+0

Vâng, sai lầm của tôi, đó là chính xác. Tôi cho rằng bạn chỉ có thể sử dụng cuộc gọi chặn tại điểm đó. Ngoài ra, 'kết quả' khối, do đó, không có nhu cầu (mặc dù không có vấn đề với) kêu gọi 'chờ đợi' đầu tiên. – Servy

+0

Không phải lo lắng.Tốt điểm về kết quả, tôi quên điều đó. –

Các vấn đề liên quan