2013-08-15 5 views
6

पहले पृष्ठभूमि की एक छोटी सी जानकारी। मैं WinRT पर निष्पादन के लिए उपयुक्त मौजूदा सी # लाइब्रेरी कोड बनाने की प्रक्रिया में हूं। इस कोड के एक छोटे से हिस्से के रूप में गहरे नीचे एक छोटी फ़ाइल आईओ करने की जरूरत है, हमने पहले चीजों को सिंक्रोनस रखने और मुख्य थ्रेड को रोकने के लिए Task.Wait() का उपयोग करने की कोशिश की जब तक कि सभी आईओ नहीं किया गया।कार्य क्यों करता है। गैर-यूई धागे पर डेडलॉक की प्रतीक्षा न करें? या मैं बस भाग्यशाली हूँ?

निश्चित रूप से, हम जल्दी से पता चला कि एक डेडलॉक की ओर जाता है।

तब मैंने इसे "एसिंक्रोनस" बनाने के लिए प्रोटोटाइप में बहुत सारे कोड को बदल दिया। यही है, मैं async डालने और कीवर्ड का इंतजार कर रहा था, और मैं तदनुसार विधि वापसी प्रकार बदल रहा था। यह बहुत काम था - वास्तव में बहुत बेवकूफ काम - लेकिन मुझे प्रोटोटाइप इस तरह से काम कर रहा था।

तो मैं एक प्रयोग किया था, और मैं एक अलग थ्रेड पर प्रतीक्षा बयान साथ मूल कोड भाग गया:

System.Threading.Tasks.Task.Run(()=> Draw(..., cancellationToken) 

कोई गतिरोध!

अब मैं गंभीरता से उलझन में हूं, क्योंकि मैंने सोचा था कि मुझे समझ में आया कि मुझे एसिंक प्रोग्रामिंग कैसे काम करती है। हमारा कोड अभी तक कॉन्फ़िगरएवाइट (झूठा) का उपयोग नहीं करता है। तो सभी प्रतीक्षा बयानों को उसी संदर्भ में जारी रखना चाहिए जैसा उन्हें शामिल किया गया था। ठीक है? I माना जाता है जिसका अर्थ है: वही धागा। अब अगर इस धागे ने "प्रतीक्षा करें" का आह्वान किया है, तो इससे डेडलॉक भी हो सकता है। लेकिन ऐसा नहीं है।

क्या आपके पास कोई स्पष्ट रॉक-ठोस स्पष्टीकरण है?

इसका उत्तर यह निर्धारित करेगा कि क्या मैं वास्तव में बहुत सशर्त एसिंक/कीवर्ड की प्रतीक्षा करके हमारे कोड को गड़बड़ कर दूंगा, या फिर मैं इसे साफ रखूंगा और केवल एक थ्रेड का उपयोग करूंगा जो प्रतीक्षा() और वहाँ। यदि निरंतरता अनियंत्रित गैर-अवरुद्ध धागे से चलती है, तो चीजें ठीक होनी चाहिए। हालांकि, अगर वे यूआई थ्रेड द्वारा चलाए जाते हैं, तो निरंतरता कम्प्यूटेशनल रूप से महंगा होने पर हम परेशानी में पड़ सकते हैं।

मुझे उम्मीद है कि यह मुद्दा स्पष्ट है। यदि नहीं, तो कृपया मुझे बताएं।

उत्तर

7

मैं अपने ब्लॉग में, मैं जहां समझाने वास्तव में क्या संदर्भ है पर एक async/await intro है:

यह SynchronizationContext.Current है जब तक कि यह null है, जिस स्थिति में यह TaskScheduler.Current है। नोट: यदि कोई वर्तमान TaskScheduler नहीं है, तो TaskScheduler.CurrentTaskScheduler.Default जैसा ही है, जो थ्रेड पूल कार्य शेड्यूलर है।

आज के कोड में, यह आमतौर पर नीचे आता है कि आपके पास SynchronizationContext है या नहीं; कार्य शेड्यूलर आज पूरी तरह से उपयोग नहीं किए जाते हैं (लेकिन शायद भविष्य में अधिक आम हो जाएंगे)। मेरे पास article on SynchronizationContext है जो वर्णन करता है कि यह कैसे काम करता है और .NET द्वारा प्रदान किए गए कुछ कार्यान्वयन।

विनरेट और अन्य यूआई फ्रेमवर्क (विनफॉर्म, डब्ल्यूपीएफ, सिल्वरलाइट) सभी अपने मुख्य यूआई थ्रेड के लिए SynchronizationContext प्रदान करते हैं। यह संदर्भ केवल एक थ्रेड का प्रतिनिधित्व करता है, इसलिए यदि आप अवरोधन और असीमित कोड मिश्रण करते हैं, तो आप जल्दी से डेडलॉक्स का सामना कर सकते हैं। मैं वर्णन करता हूं कि यह अधिक जानकारी in a blog post में क्यों होता है, लेकिन सारांश में इसका कारण यह है कि async विधि SynchronizationContext (इस मामले में, UI थ्रेड पर निष्पादन फिर से शुरू करने) को फिर से दर्ज करने का प्रयास कर रही है, लेकिन यूआई थ्रेड अवरुद्ध है पूरा करने के लिए async विधि की प्रतीक्षा कर रहा है।

थ्रेड पूल में SynchronizationContext (या सामान्यतः TaskScheduler) नहीं है। तो यदि आप थ्रेड पूल थ्रेड पर निष्पादित कर रहे हैं और एसिंक्रोनस कोड पर ब्लॉक करते हैं, तो यह डेडलॉक नहीं होगा। ऐसा इसलिए है क्योंकि कब्जा संदर्भ थ्रेड पूल संदर्भ है (जो किसी विशेष धागे से बंधे नहीं है), इसलिए async विधि इसके संदर्भ को फिर से दर्ज कर सकती है (केवल थ्रेड पूल थ्रेड पर चलकर) जबकि एक और थ्रेड पूल थ्रेड अवरुद्ध हो रहा है इसे पूरा करने के लिए।

इसका जवाब निर्धारित करेगा कि क्या मैं वास्तव में सशर्त async का एक बहुत डालने से हमारे कोड अप खिलवाड़ के माध्यम से जाना होगा/इंतजार कीवर्ड, या कि क्या मैं यह साफ रखना होगा और बस (एक धागा है कि एक प्रतीक्षा करता है का उपयोग करें) इधर - उधर।

यदि आपका कोड async है, तो यह बिल्कुल गन्दा नहीं दिखना चाहिए। मुझे यकीन नहीं है कि "सशर्त" से आपका क्या मतलब है; मैं बस इसे सभी async बना दूंगा। await में "फास्ट पथ" कार्यान्वयन है जो ऑपरेशन पहले ही पूरा हो चुका है तो यह तुल्यकालिक बनाता है।

एसिंक्रोनस कोड एक पृष्ठभूमि धागा का उपयोग कर पर अवरोधित करना संभव है, लेकिन यह कुछ चेतावनियां है:

  1. आप ताकि आप यूआई के लिए बहुत कुछ नहीं कर सकते, यूआई संदर्भ नहीं है।
  2. आपको अभी भी यूआई थ्रेड में "सिंक अप" करना होगा, और आपका यूआई थ्रेड ब्लॉक नहीं करना चाहिए (उदा।, await Task.Run(..), Task.Run(..).Wait() नहीं होना चाहिए)। यह WinRT ऐप्स के लिए विशेष रूप से सच है।
+0

जानकारी के लिए धन्यवाद। यह काफी बताता है। –

+0

चीजों को गड़बड़ करने के लिए: यह मौजूदा लाइब्रेरी कोड है जिसे एकाधिक (.Net) प्लेटफार्मों पर लक्षित किया जाता है, न केवल WinRT। हम सभी प्लेटफार्मों के लिए एक ही कोड का उपयोग करना चाहते हैं, इसलिए हमें प्लेटफ़ॉर्म के लिए प्रतीक्षा/एसिंक से बचने के लिए #if का उपयोग करना होगा, जहां यह उपलब्ध नहीं है। इसके अतिरिक्त, हम सभी मौजूदा एपीआई को एसिंक्रोनस में बदल नहीं सकते हैं, क्योंकि इससे मौजूदा ग्राहकों के लिए चीजें तोड़ जाएंगी। ये सभी #if स्टेटमेंट कोड को थोड़ा कम पठनीय बनाते हैं। –

+0

मुझे एसिंक का _idea_ लगता है/काफी आकर्षक और समझदार इंतजार कर रहा है। हालांकि, माइक्रोसॉफ्ट द्वारा प्रदान किया गया वास्तविक कार्यान्वयन मुझे लगता है कि परेशानी है। तथ्य यह है कि इन संरचनाओं का अर्थशास्त्र उस संदर्भ पर निर्भर करता है जिसमें धागा शुरू किया गया था, मुझे लगता है कि एक गलती है। मुख्य रूप से क्योंकि प्रोग्राम कोड के अर्थशास्त्र को जितना संभव हो सके रनटाइम संदर्भ पर निर्भर होना चाहिए (इसके बारे में कठिन कारण)। मैं समझ सकता हूं कि माइक्रोसॉफ्ट विकल्प जोड़ना चाहता है, लेकिन मुझे लगता है कि इंतजार और सिंक के सीधा उपयोग इस तरह के मतभेदों को पेश नहीं करना चाहिए। –

संबंधित मुद्दे