2012-11-09 16 views
5

समाप्त नहीं होता है, मैं एसिंक कार्य (.NET 4.5 पर) शुरू करने की कोशिश कर रहा हूं जो वेब पेज की सामग्री डाउनलोड करता है, लेकिन किसी भी तरह यह कार्य कभी खत्म नहीं होता है।Async कार्य

मेरे 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() कार्य कभी खत्म नहीं होता है - अगली पंक्ति पर ब्रेकपॉइंट कभी नहीं पहुंचता है। मुझे कोई अपवाद नहीं है, एप्लिकेशन चल रहा है। कोई सुझाव?

उत्तर

9

यह एक मानक डेडलॉक स्थिति है जब आप async ऑपरेशन शुरू करते हैं और फिर लौटा कार्य पर अवरुद्ध करते हैं।

Here एक ब्लॉग पोस्ट विषय पर चर्चा है।

असल में, await कॉल कि निरंतरता यह कार्य के ऊपर तारों संदर्भ आप में मूल रूप से थे (जो बहुत उपयोगी है) में चलेगा, लेकिन यह सुनिश्चित करता है कि आपके पास उस उसी संदर्भ में यह ब्लॉकिंग में Wait, इसलिए निरंतरता बुला रहे हैं कभी नहीं चलता है, और उस निरंतरता को प्रतीक्षा करने के लिए दौड़ने की आवश्यकता होती है। क्लासिक डेडलॉक।

फिक्स के लिए; आम तौर पर इसका मतलब है कि आपको एसिंक ऑपरेशन पर अवरुद्ध इंतजार नहीं करना चाहिए; यह पूरे सिस्टम के डिजाइन के विपरीत है। आपको, "सभी तरह से async" चाहिए। इस मामले में इसका अर्थ यह होगा कि GetPageData को string की बजाय Task<string> वापस करना चाहिए, और उन कार्यों पर प्रतीक्षा करने के बजाय आपको 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(); ; 
    } 
} 
+0

अच्छे उत्तर के लिए धन्यवाद। क्या आप यह भी समझा सकते हैं कि मुझे कॉलिंग कोड में प्रतिक्रिया कैसे संभालना चाहिए? –

+0

@YuriyPogrebnyak मैं पहले से ही संपादन की प्रक्रिया में था। – Servy

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

कार्य को वापस करने और कॉलिंग कोड में एसिंक/प्रतीक्षा के साथ इसे संभालना बेहतर होगा।

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

कैसा है? मैं प्रतीक्षा कॉल से पहले एक async/प्रतीक्षा शुरू नहीं कर रहा हूँ। मैं कार्य पर एक मानक प्रतीक्षा कॉल का उपयोग कर रहा हूँ। शायद मुझे कुछ याद आ रहा है, लेकिन यह प्रतीक्षा विधि का उपयोग करने के एसडीके उदाहरणों के लगभग समान दिखता है। –

+0

हाँ, मेरी गलती, यह सही है। मुझे लगता है कि आप उस बिंदु पर अवरुद्ध कॉल का उपयोग कर सकते हैं। साथ ही, 'परिणाम' ब्लॉक, इसलिए पहले 'प्रतीक्षा करें' को कॉल करने की कोई ज़रूरत नहीं है (हालांकि इसमें कोई समस्या नहीं है)। – Servy

+0

कोई चिंता नहीं।परिणाम पर अच्छा बिंदु, मैं भूल गया। –