2012-04-23 11 views
50

टीपीएल & async के बीच अंतर को समझने की कोशिश कर रहा है/थ्रेड निर्माण की प्रतीक्षा करते समय प्रतीक्षा करें।टीपीएल और एसिंक/प्रतीक्षा (थ्रेड हैंडलिंग) के बीच अंतर

मेरा मानना ​​है कि टीपीएल (टास्कफैक्टरी.स्टार्टन्यू) थ्रेडपूल के समान काम करता है। क्यूयूयूसर वर्कइटम जिसमें यह थ्रेड पूल में थ्रेड पर काम करता है। यह निश्चित रूप से तब तक है जब तक आप TaskCreationOptions.LongRunning का उपयोग नहीं करते जो एक नया धागा बनाते हैं।

मैंने सोचा था कि async/इंतजार इसी तरह तो अनिवार्य रूप से काम करेगा:

TPL:

Factory.StartNew(() => DoSomeAsyncWork()) 
.ContinueWith( 
    (antecedent) => { 
     DoSomeWorkAfter(); 
    },TaskScheduler.FromCurrentSynchronizationContext()); 

Async/इंतजार:

await DoSomeAsyncWork(); 
DoSomeWorkAfter(); 

समान होगा। जो मैं पढ़ रहा हूं उससे यह लगता है कि एसिंक/प्रतीक्षा केवल "कभी-कभी" एक नया धागा बनाता है। तो यह एक नया धागा कब बनाता है और जब यह एक नया धागा नहीं बनाता है? यदि आप आईओ पूरा करने वाले बंदरगाहों से निपट रहे थे तो मैं इसे एक नया धागा बनाने के लिए नहीं देख सकता लेकिन अन्यथा मुझे लगता है कि इसे करना होगा। मुझे लगता है कि FromCurrentSynchronizationContext की मेरी समझ हमेशा थोड़ी अस्पष्ट थी। मैं हमेशा यह था, संक्षेप में, यूआई धागा था।

+2

असल में, टास्क क्रिएशनऑप्शन। लोंगरिंगिंग "नए थ्रेड" की गारंटी नहीं देता है। प्रति एमएसडीएन, * "LongRunning" विकल्प केवल शेड्यूलर को संकेत देता है; यह एक समर्पित धागे की गारंटी नहीं देता है। * मैंने पाया कि कठिन तरीके से। – eduncan911

+0

@ eduncan911 हालांकि दस्तावेज के बारे में आप जो कहते हैं वह सही है, मैंने थोड़ी देर पहले टीपीएल स्रोत कोड देखा और मुझे पूरा यकीन है कि वास्तव में एक नया समर्पित धागा हमेशा बनाया जाता है जब 'टास्क क्रिएशनऑप्शन। लोंगरिंग' निर्दिष्ट किया जाता है। –

+0

@ZaidMasud: आप एक और रूप लेना चाह सकते हैं।मुझे पता है कि यह धागे को पूल कर रहा था क्योंकि 'थ्रेड। कंटेंट थ्रेड। आईएस थ्रेडपूल थ्रेड' कुछ सौ मिलीसेकंड के शॉर्ट-रनिंग थ्रेड के लिए सच हो रहा था। थ्रेडस्टैटिक चर का उल्लेख नहीं करना मैं कई धागे में खून बह रहा था, जिससे सभी प्रकार के हावोक होते थे। मुझे एक समर्पित धागे की गारंटी के लिए अपने कोड को कई थ्रेड() एस, पुराने स्कूल के रास्ते पर नए बल में मजबूर करना पड़ा। दूसरे शब्दों में, मैं समर्पित धागे के लिए टास्क फैक्टरी का उपयोग नहीं कर सका। वैकल्पिक रूप से, आप अपना 'टास्कशेड्यूलर' लागू कर सकते हैं जो हमेशा एक समर्पित धागा देता है। – eduncan911

उत्तर

64

मेरा मानना ​​है कि TPL (TaskFactory.Startnew) समान काम करता है कि मैं में ThreadPool.QueueUserWorkItem को थ्रेड पूल में धागे पर काम कतारबद्ध करता है।

Pretty much

जो मैं पढ़ रहा हूं उससे यह लगता है कि एसिंक/प्रतीक्षा केवल "कभी-कभी" एक नया धागा बनाता है।

वास्तव में, यह कभी नहीं करता है। यदि आप मल्टीथ्रेडिंग चाहते हैं, तो आपको इसे स्वयं लागू करना होगा। विधि है जो Task.Factory.StartNew के लिए सिर्फ शॉर्टेंड है, और यह शायद थ्रेड पूल पर कार्य शुरू करने का सबसे आम तरीका है।

यदि आप आईओ पूरा करने वाले बंदरगाहों से निपट रहे थे तो मैं इसे एक नया धागा बनाने के लिए नहीं देख सकता लेकिन अन्यथा मुझे लगता है कि इसे करना होगा।

बिंगो। तो Stream.ReadAsync जैसी विधियां वास्तव में एक आईओसीपी के आसपास Task रैपर बनाएंगी (यदि Stream में आईओसीपी है)।

आप कुछ गैर-आई/ओ, गैर-सीपीयू "कार्य" भी बना सकते हैं। एक साधारण उदाहरण Task.Delay है, जो एक कार्य देता है जो कुछ समय अवधि के बाद पूरा हो जाता है।

अच्छी बात के बारे में async/await कि आप थ्रेड पूल के लिए कुछ काम कतार कर सकते हैं (जैसे, Task.Run), कुछ आई/ओ बाध्य आपरेशन (जैसे, Stream.ReadAsync), करते हैं और कुछ अन्य आपरेशन करना (जैसे, Task.Delay) ... और वे सभी कार्य हैं! उन्हें Task.WhenAll जैसे संयोजनों में इंतजार या उपयोग किया जा सकता है।

Task लौटने वाली कोई भी विधि await एड हो सकती है - यह async विधि नहीं होनी चाहिए। तो Task.Delay और I/O- बाध्य संचालन केवल कार्य को बनाने और पूरा करने के लिए TaskCompletionSource का उपयोग करें - थ्रेड पूल पर केवल एक ही चीज होने पर वास्तविक कार्य पूरा होने पर वास्तविक कार्य पूरा होता है (टाइमआउट, I/O पूरा करने आदि)।

मुझे लगता है कि FromCurrentSynchronizationContext की मेरी समझ हमेशा थोड़ी अस्पष्ट थी। मैं हमेशा यह था, संक्षेप में, यूआई धागा था।

मैंने SynchronizationContext पर an article लिखा था। अधिकांश समय, SynchronizationContext.Current:

  • एक यूआई संदर्भ है यदि वर्तमान धागा यूआई थ्रेड है।
  • एक एएसपी.NET अनुरोध संदर्भ है यदि वर्तमान थ्रेड एएसपी.NET अनुरोध की सेवा कर रहा है।
  • अन्यथा थ्रेड पूल संदर्भ है।

किसी भी धागा कर सकते हैं अपनी ही SynchronizationContext निर्धारित करते हैं, तो वहाँ उपरोक्त नियमों के अपवाद हैं।

ध्यान दें कि डिफ़ॉल्ट Task awaiter वर्तमान SynchronizationContextपर async विधि के शेष निर्धारित करेगा अगर यह रिक्त नहीं है; अन्यथा यह वर्तमान TaskScheduler पर चला जाता है। यह आज इतना महत्वपूर्ण नहीं है, लेकिन निकट भविष्य में यह एक महत्वपूर्ण भेद होगा।

मैंने अपने ब्लॉग पर अपना खुद का async/await intro लिखा, और स्टीफन टब ने हाल ही में एक उत्कृष्ट async/await FAQ पोस्ट किया।

"मल्टीथ्रेडिंग" बनाम "समरूपता" के संबंध में, this related SO question देखें। मैं कहूंगा कि async समवर्तीता को सक्षम बनाता है, जो बहुप्रचारित हो सकता है या नहीं। समवर्ती प्रसंस्करण करने के लिए await Task.WhenAll या await Task.WhenAny का उपयोग करना आसान है, और जब तक आप स्पष्ट रूप से थ्रेड पूल (उदाहरण के लिए, Task.Run या ConfigureAwait(false)) का उपयोग नहीं करते हैं, तो आप एक ही समय में कई समवर्ती परिचालन प्रगति कर सकते हैं (उदाहरण के लिए, एकाधिक I/O या अन्य Delay जैसे प्रकार) - और उनके लिए कोई थ्रेड आवश्यक नहीं है। मैं इस तरह के परिदृश्य के लिए "सिंगल-थ्रेडेड कॉन्सुरेंसी" शब्द का उपयोग करता हूं, हालांकि एएसपी.नेट होस्ट में, आप वास्तव में "शून्य -थ्रेड समेकन" के साथ समाप्त हो सकते हैं। जो बहुत प्यारा है।

+1

अच्छा जवाब। मैं http://www.infoq.com/articles/Async-API- डिज़ाइन और यह उत्कृष्ट प्रस्तुति भी अनुशंसा करता हूं: http://channel9.msdn.com/Events/TechEd/Europe/2013/DEV-B318। – Philippe

+0

पहला लिंक मर चुका है। –

+0

@FelipeDeveza फिक्स्ड, धन्यवाद! –

8

async/इंतजार है मूल रूप से ContinueWith विधियों (Continuation Passing Style में निरंतरता) को सरल

यह संगामिति परिचय नहीं करता है - आप अभी भी क्या करना है कि अपने आप को (या एक रूपरेखा विधि के Async संस्करण का उपयोग करें।)

तो, सी # 5 संस्करण होगा:

await Task.Run(() => DoSomeAsyncWork()); 
DoSomeWorkAfter(); 
+0

तो यह उपरोक्त मेरे उदाहरण में DoSomeAsyncWork (async/प्रतीक्षा संस्करण) कहां चल रहा है? यदि यह यूआई थ्रेड पर चल रहा है तो यह कैसे ब्लॉक नहीं करता है? – coding4fun

+1

आपका प्रतीक्षा उदाहरण संकलित नहीं होगा अगर 'DoSomeWorkAsync()' शून्य लौटाता है या ऐसा कुछ जो प्रतीक्षा योग्य नहीं है। आपके पहले उदाहरण से, मैंने माना कि यह एक अनुक्रमिक विधि है जिसे आप एक अलग थ्रेड पर चलाना चाहते हैं। यदि आपने इसे 'कार्य' वापस करने के लिए बदल दिया है, तो समेकन शुरू किए बिना, तो हाँ यह अवरुद्ध होगा। इस अर्थ में कि यह अनुक्रमिक रूप से निष्पादित होगा और यूआई थ्रेड पर सामान्य कोड की तरह होगा। 'प्रतीक्षा' केवल उपज करता है यदि विधि एक प्रतीक्षा योग्य लौटाती है जो अभी तक पूरा नहीं हुआ है। –

+0

अच्छी तरह से, मैं यह नहीं कहूंगा कि यह जहां भी इसे चलाने के लिए चुनता है वहां चलता है। आपने DoSomeAsyncWork में कोड निष्पादित करने के लिए कार्य का उपयोग किया है, इसलिए इस मामले में आपका काम थ्रेडपूल थ्रेड पर किया जाएगा। –