2012-10-19 10 views
23

में एकाधिक प्रतीक्षा कार्यों के साथ लटक रहा है। नीचे दिए गए कोड का एक सरलीकृत संस्करण नीचे दिया गया है। जब मैं इसे कंसोल एप्लिकेशन में चलाता हूं, तो यह अपेक्षा के अनुसार काम करता है। सभी प्रश्न समानांतर में चलाए जाते हैं और Task.WaitAll() लौटते हैं जब वे पूरी हो जाते हैं।कार्य। ASP.NET

हालांकि, जब यह कोड किसी वेब एप्लिकेशन में चलता है, तो अनुरोध केवल लटकता है। जब मैं एक डीबगर संलग्न करता हूं और सभी को तोड़ता हूं, तो यह दिखाता है कि निष्पादन Task.WaitAll() पर प्रतीक्षा है। और पहला कार्य पूरा हो गया है, लेकिन अन्य कभी खत्म नहीं होते हैं।

मुझे पता नहीं लगा सकता कि यह ASP.NET में चलते समय क्यों लटकता है, लेकिन कंसोल एप्लिकेशन में ठीक काम करता है।

public Foo[] DoWork(int[] values) 
{ 
    int count = values.Length; 
    Task[] tasks = new Task[count]; 

    for (int i = 0; i < count; i++) 
    { 
     tasks[i] = GetFooAsync(values[i]); 
    } 

    try 
    { 
     Task.WaitAll(tasks); 
    } 
    catch (AggregateException) 
    { 
     // Handle exceptions 
    } 

    return ... 
} 

public async Task<Foo> GetFooAsync(int value) 
{ 
    Foo foo = null; 

    Func<Foo, Task> executeCommand = async (command) => 
    { 
     foo = new Foo(); 

     using (SqlDataReader reader = await command.ExecuteReaderAsync()) 
     { 
      ReadFoo(reader, foo); 
     } 
    }; 

    await QueryAsync(executeCommand, value); 

    return foo; 
} 

public async Task QueryAsync(Func<SqlCommand, Task> executeCommand, int value) 
{ 
    using (SqlConnection connection = new SqlConnection(...)) 
    { 
     connection.Open(); 

     using (SqlCommand command = connection.CreateCommand()) 
     { 
      // Set up query... 

      await executeCommand(command); 

      // Log results... 

      return; 
     } 
    }   
} 

उत्तर

46

बजाय Task.WaitAll आप await Task.WhenAll उपयोग करने के लिए की जरूरत है।

एएसपी.नेट में आपके पास वास्तविक सिंक्रनाइज़ेशन संदर्भ है। इसका मतलब है कि सभी await कॉल के बाद आपको निरंतरता को प्रभावी ढंग से निष्पादित करने के लिए उस संदर्भ में मार्शल किया जाएगा (इन निरंतरताओं को प्रभावी ढंग से क्रमबद्ध करना)। एक कंसोल ऐप में कोई सिंक्रनाइज़ेशन संदर्भ नहीं है, इसलिए सभी निरंतरताएं थ्रेड पूल पर भेजी जाती हैं। अनुरोध के संदर्भ में Task.WaitAll का उपयोग करके आप इसे अवरुद्ध कर रहे हैं, जो इसे अन्य सभी कार्यों से निरंतरता को संभालने के लिए उपयोग करने से रोक रहा है।

यह भी ध्यान दें कि एएसपी ऐप में एसिंक/प्रतीक्षा के प्राथमिक लाभों में से एक अनुरोध को संभालने के लिए उपयोग किए जा रहे थ्रेड पूल थ्रेड को अवरुद्ध करता है। यदि आप Task.WaitAll का उपयोग करते हैं तो आप उस उद्देश्य को हरा रहे हैं।

इस परिवर्तन को करने का एक दुष्प्रभाव यह है कि अवरुद्ध करने वाले ऑपरेशन से लेकर एक प्रतीक्षा ऑपरेशन अपवादों को स्थानांतरित करके अलग-अलग प्रचार किया जाएगा। AggregateException फेंकने की बजाए यह अंतर्निहित अपवादों में से एक को फेंक देगा।

+1

+1। हालांकि मैं "मुख्य धागे" के बजाय "अनुरोध संदर्भ" शब्द का उपयोग करूंगा। –

+0

@ स्टीफन क्लेरी हाँ, यह बहस योग्य था। मैंने उद्धरणों में मुख्य धागा लगाया क्योंकि यह पूरे एप्लिकेशन का मुख्य धागा नहीं है, लेकिन इसे "उस अनुरोध के मुख्य धागे" के रूप में माना जा सकता है। यह मेरे दिमाग में समस्या के बारे में सोचने का एक उपयोगी तरीका है। – Servy

+4

एक महत्वपूर्ण चेतावनी यह है कि कार्य का इंतजार है। जब सभी कुल अपवाद नहीं फेंकेंगे; आंतरिक अपवादों में से केवल एक ही प्रचारित किया जाएगा। यदि आप संपूर्ण समेकन अपवाद की जांच करना चाहते हैं, तो आपको कार्य से लौटे हुए कार्य का संदर्भ संग्रहीत करना होगा। जब भी और इसकी अपवाद संपत्ति की स्पष्ट रूप से जांच करें। –