2013-04-25 6 views
7

में क्रियान्वित कर रहे हैं क्यों कोड के इस टुकड़े के बारे में:Async कॉलबैक WPF यूआई धागा

static async Task<string> testc() 
{ 
    Console.WriteLine("helo async " + Thread.CurrentThread.ManagedThreadId); 
    await Task.Run(() => { 
     Thread.Sleep(1000); 
     Console.WriteLine("task " + Thread.CurrentThread.ManagedThreadId); 
    }); 
    Console.WriteLine("callback "+Thread.CurrentThread.ManagedThreadId); 
    return "bob"; 
} 

static void Main(string[] args) 
{ 
    Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId); 
    testc(); 
    Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId); 
    Thread.Sleep(2000); 
    Console.ReadLine(); 
} 

मैं मिल निम्नलिखित उत्पादन:

helo sync 10 
helo async 10 
over10 
task 11 
callback **11** 

कौन सा ठीक है: इंतजार के बाद कोड का टुकड़ा में निष्पादित किया जाता है कार्य के रूप में एक ही धागा।

अब, अगर मैं एक WPF आवेदन में यह कार्य करें:

private void Button_Click_1(object sender, RoutedEventArgs e) 
{ 
    Console.WriteLine("helo sync " + Thread.CurrentThread.ManagedThreadId); 
    testc(); 
    Console.WriteLine("over" + Thread.CurrentThread.ManagedThreadId); 
    Thread.Sleep(2000); 
    Console.ReadLine(); 
} 

यह उत्पादन निम्नलिखित उत्पन्न:

helo sync 8 
helo async 8 
over8 
task 9 
callback **8** 

हम कहाँ कोड देख सकते हैं के बाद यूआई धागा में मार डाला का इंतजार है। खैर, यह बहुत अच्छा है क्योंकि यह अवलोकन संग्रह आदि में हेरफेर करने में सक्षम बनाता है ... लेकिन मैं सोच रहा था "क्यों?" "मैं वही कैसे कर सकता हूं?" क्या यह कुछ कार्यक्षेत्र व्यवहार से संबंधित है? क्या यह .NET Framework में हार्ड-कोड किया गया है?

Thx किसी भी विचार के लिए आप जमा कर सकते हैं।

उत्तर

7

कारण यह है कि टास्क.रुन SynchronizationContext पर कब्जा करेगा यदि कोई मौजूद है क्योंकि यह यूआई थ्रेड से कार्य शुरू करते समय एक WPF ऐप में है। कार्य तब यूआई थ्रेड पर कॉलबैक को क्रमबद्ध करने के लिए SynchronizationContext का उपयोग करेगा। हालांकि, यदि कोई कंसोल ऐप में कोई संदर्भ उपलब्ध नहीं है, तो कॉलबैक एक अलग थ्रेड पर होगा।

स्टीफन टब ने इसे blog entry में वर्णित किया है।

बीटीडब्ल्यू, का उपयोग करते समय सावधान रहें, कभी भी थ्रेड का उपयोग न करें। कार्य में सोएं।यह अजीब व्यवहार कर सकता है क्योंकि एक कार्य एक थ्रेड से बंधे नहीं जा सकता है। इसके बजाय कार्य का उपयोग करें। डेले।

+0

+1 को आपके बीटीडब्ल्यू पर अधिक जोर देने की आवश्यकता है। इस पूरे उत्तर के लिए – Phill

2

लेकिन मैं सोच रहा था "क्यों?"

आप उत्तर दिया है कि अपने आप को:

खैर, यह महान है, क्योंकि यह नमूदार संग्रह आदि से छेड़छाड़ के लिए सक्षम बनाता है ...

async के पूरे मुद्दे asynchrony आसान बनाना है साथ काम करने के लिए - ताकि आप "सिंक्रोनस-लुकिंग" कोड लिख सकें जो वास्तव में असीमित है। उसमें अक्सर एक एसिंक विधि के लिए एक संदर्भ (जैसे यूआई थ्रेड) के अंदर रहना चाहते हैं - जब आपको कुछ इंतजार करने की आवश्यकता होती है तो विधि को "रोकना" विधि (यूआई थ्रेड को अवरुद्ध किए बिना)।

"मैं वही कैसे कर सकता हूं?"

यह स्पष्ट नहीं है कि आपका यहां क्या मतलब है। असल में, Task के लिए प्रतीक्षा करने योग्य पैटर्न का कार्यान्वयन TaskScheduler.FromCurrentSynchronizationContext() का उपयोग करता है ताकि कॉलबैक पोस्ट करने के लिए कौन सा शेड्यूलर चालू हो - जब तक कि आपने इस व्यवहार से स्पष्ट रूप से ऑप्ट आउट करने के लिए ConfigureAwait(false) नहीं कहा है। तो इस तरह यह इसका प्रबंधन करता है ... चाहे आप "वही कर सकें" चाहे आप जो भी करने की कोशिश कर रहे हैं उस पर निर्भर करता है।

प्रतीक्षा करने योग्य पैटर्न के अधिक विवरण के लिए async/await FAQ में "क्या प्रतीक्षा कर रहे हैं" प्रश्न देखें।

+0

"मैं वही कैसे कर सकता हूं?" मुझे लगता है कि उसका मतलब है: "मैं WPF का उपयोग किए बिना डब्ल्यूपीएफ व्यवहार कैसे प्राप्त करूं?" –

+0

@ jdv-jandeVaan: अगर वह अर्थ था, तो यह बेहद अस्पष्ट है। वैसे भी, उम्मीद है कि मैंने जो विवरण दिया है वह ओपी को आगे बढ़ाने के लिए पर्याप्त जानकारी देगा। यदि आवश्यक हो तो वे हमेशा अधिक जानकारी मांग सकते हैं। –

+0

ठीक है .. ठीक है। मैं वास्तव में ऐसा नहीं करना चाहता हूं। मैं सोच रहा था कि कॉलबैक डिस्पैचर को कैसे भेजा जाता है। इन्वोक विधि ... और यदि मैं कर सकता हूं, अंततः, एक समान चीज़ कर सकता हूं या यदि फ्रेमवर्क में इसे हार्ड कोड किया गया था। आपके उत्तर के लिए Thx – Kek

0

आप मेरी async/await intro सहायक पा सकते हैं। अन्य उत्तरों लगभग सही हैं।

जब आप await एक Task कि अभी तक पूरा नहीं किया है, डिफ़ॉल्ट एक "संदर्भ" द्वारा कब्जा कर लिया है जो जब Task कम्प्लिट्स विधि को फिर से शुरू करने के लिए प्रयोग किया जाता है। यह "संदर्भ" SynchronizationContext.Currentहै जब तक कि यह शून्य न हो, इस स्थिति में यह TaskScheduler.Current है।

नोट की स्थिति काम करने के लिए इस के लिए आवश्यक:

  • "जब आप await ..." - यदि आप Task.ContinueWith साथ मैन्युअल रूप से एक निरंतरता अनुसूची, उदा, कोई संदर्भ कब्जा किया जाता है। (SynchronizationContext.Current == null ? TaskSchedler.Current : TaskScheduler.FromCurrentSynchronizationContext()) जैसे कुछ का उपयोग करके आपको इसे स्वयं करना होगा।
  • "... await एक Task ..." - इस व्यवहार Task प्रकार के लिए await व्यवहार का हिस्सा है। अन्य प्रकार एक समान कैप्चर कर सकते हैं या नहीं भी कर सकते हैं।
  • "... अभी तक पूरा कर लिया है कि नहीं ..." - अगर Task पहले से ही समय से पूरा हो गया है यह await एड है, async विधि तुल्यकालिक जारी रहेगा। तो उस मामले में संदर्भ को पकड़ने की कोई जरूरत नहीं है।
  • "... डिफ़ॉल्ट रूप से ..." - यह डिफ़ॉल्ट व्यवहार है और बदला जा सकता है। विशेष रूप से, Task.ConfigureAwait विधि पर कॉल करें और continueOnCapturedContext पैरामीटर के लिए false पास करें। यह विधि एक प्रतीक्षा योग्य प्रकार (Task नहीं) देता है जो कैप्चर किए गए संदर्भ पर जारी नहीं रहेगा यदि इसका पैरामीटर false था।
+0

Thx। इसमें से अधिकांश (या कम से कम मेरे प्रश्न का उत्तर देने के लिए पर्याप्त) @ जैकोब क्रिस्टेंसेन के जवाब में लिंक में था, लेकिन मैं इस पूर्ण दृष्टिकोण की प्रशंसा करता हूं! – Kek

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