2012-10-22 19 views
25

नई async के साथ/सी # में कीवर्ड का इंतजार है, अब जिस तरह से (और जब) आप ThreadStatic डेटा का उपयोग करने के प्रभावों देखते हैं, क्योंकि कॉलबैक प्रतिनिधि एक async के लिए एक अलग थ्रेड पर निष्पादित किया जाता है ऑपरेशन शुरू हुआ। उदाहरण के लिए, निम्नलिखित सरल कंसोल अनुप्रयोग: की रेखा के साथasync साथ ThreadStatic वैरिएबल का उपयोग/इंतजार

[ThreadStatic] 
private static string Secret; 

static void Main(string[] args) 
{ 
    Start().Wait(); 
    Console.ReadKey(); 
} 

private static async Task Start() 
{ 
    Secret = "moo moo"; 
    Console.WriteLine("Started on thread [{0}]", Thread.CurrentThread.ManagedThreadId); 
    Console.WriteLine("Secret is [{0}]", Secret); 

    await Sleepy(); 

    Console.WriteLine("Finished on thread [{0}]", Thread.CurrentThread.ManagedThreadId); 
    Console.WriteLine("Secret is [{0}]", Secret); 
} 

private static async Task Sleepy() 
{ 
    Console.WriteLine("Was on thread [{0}]", Thread.CurrentThread.ManagedThreadId); 
    await Task.Delay(1000); 
    Console.WriteLine("Now on thread [{0}]", Thread.CurrentThread.ManagedThreadId); 
} 

इच्छा उत्पादन कुछ:

Started on thread [9] 
Secret is [moo moo] 
Was on thread [9] 
Now on thread [11] 
Finished on thread [11] 
Secret is [] 

मैं भी CallContext.SetData और CallContext.GetData का उपयोग कर के साथ प्रयोग किया और एक ही व्यवहार मिल गया है।

कुछ संबंधित प्रश्न और धागे को पढ़ने के बाद:

ऐसा लगता है कि ASP.Net तरह चौखटे स्पष्ट रूप से माइग्रेट करती धागे भर में HttpContext, लेकिन नहीं CallContext, तो शायदके उपयोग के साथ भी यही बात हो रही हैऔर await कीवर्ड?

एसिंक/प्रतीक्षा कीवर्ड के उपयोग के साथ, निष्पादन के किसी विशेष थ्रेड से जुड़े डेटा को संग्रहीत करने का सबसे अच्छा तरीका क्या है (स्वचालित रूप से!) कॉलबैक थ्रेड पर पुनर्स्थापित किया जा सकता है?

धन्यवाद,

उत्तर

9

मूल रूप से, मैं जोर देना होगा: ऐसा नहीं है। [ThreadStatic] कभी भी थ्रेड के बीच कूदने वाले कोड के साथ अच्छी तरह से खेलने वाला नहीं है।

लेकिन आपको ऐसा करने की ज़रूरत नहीं है। एक Task पहले से ही राज्य में किया जाता है - वास्तव में, यह यह 2 अलग अलग तरीकों से कर सकते हैं:

  • वहाँ एक स्पष्ट राज्य वस्तु है, जो सब कुछ आप की जरूरत है पकड़ कर सकते हैं
  • lambdas/anon-तरीकों राज्य
  • से अधिक बंदी बना सकते हैं

साथ ही, संकलक सब कुछ आप यहाँ वैसे भी आवश्यकता है:

private static async Task Start() 
{ 
    string secret = "moo moo"; 
    Console.WriteLine("Started on thread [{0}]", 
     Thread.CurrentThread.ManagedThreadId); 
    Console.WriteLine("Secret is [{0}]", secret); 

    await Sleepy(); 

    Console.WriteLine("Finished on thread [{0}]", 
     Thread.CurrentThread.ManagedThreadId); 
    Console.WriteLine("Secret is [{0}]", secret); 
} 

कोई स्थिर राज्य; धागे या एकाधिक कार्यों के साथ कोई समस्या नहीं है। यह बस काम करता है। ध्यान दें कि secret सिर्फ "स्थानीय" नहीं है; कंपाइलर ने कुछ वूडू काम किया है, जैसे यह इटरेटर ब्लॉक और कब्जे वाले चर के साथ करता है। परावर्तक जाँच हो रही है, मैं:

[CompilerGenerated] 
private struct <Start>d__0 : IAsyncStateMachine 
{ 
    // ... lots more here not shown 
    public string <secret>5__1; 
} 
+0

डब्ल्यूसीएफ के मामलों में क्या है? क्या मुझे इसके बजाय 'ऑपरेशन कॉन्टेक्स्ट' का उपयोग करना चाहिए, बशर्ते कि यह नए धागे पर माइग्रेट हो जाए? – theburningmonk

+0

@theburningmonk अगर आपका मतलब * आवृत्ति * है, तो उसे काम करना चाहिए। लेकिन मुझे संदेह है कि स्थैतिक 'ऑपरेशन कॉन्टेक्स्ट.क्यूरेंट' सही तरीके से काम करेगा। तो 'var ctx = OperationContext.Current;' शीर्ष पर (मूल धागे पर), और फिर 'ctx' को संदर्भित करें, न कि' ऑपरेशन कॉन्टेक्स्ट.क्यूरेंट' –

+0

'ताकि आप यह कह रहे हों कि जब तक आप वर्तमान' ऑपरेशन कॉन्टेक्स्ट को कैप्चर नहीं करते 'प्रतीक्षा' से पहले बंद होने पर आप 'प्रतीक्षा 'के बाद' ऑपरेशन कॉन्टेक्स्ट 'के समान उदाहरण वापस नहीं पाएंगे? – theburningmonk

7

एक ही धागे पर अमल करने के लिए एक कार्य निरंतरता हो रही एक तुल्यकालन प्रदाता की आवश्यकता है। यह एक महंगा शब्द है, सरल डायग्नोस्टिक सिस्टम के मूल्य को देखकर है। थ्रेडिंग। सिंक्रनाइज़ेशन कॉन्टेक्स्ट। डीबगर में वर्तमान।

यह मान कंसोल मोड ऐप में होगा। कोई प्रदाता नहीं है जो कंसोल मोड ऐप में किसी विशिष्ट थ्रेड पर कोड चला सकता है। केवल एक विनफॉर्म या डब्ल्यूपीएफ ऐप या एएसपी।नेट ऐप में एक प्रदाता होगा। और केवल उनके मुख्य धागे पर।

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

WindowsFormsSynchronizationContext Winforms ऐप के लिए सिंक्रनाइज़ेशन प्रदाता है। यह अनुरोध प्रेषित करने के लिए Control.Begin/Invoke() का उपयोग करता है। WPF के लिए यह डिस्पैचर सिंक्रनाइज़ेशन कॉन्टेक्स्ट क्लास है, यह अनुरोध प्रेषित करने के लिए Dispatcher.Begin/Invoke() का उपयोग करता है। एएसपी.नेट के लिए यह AspNetSynchronizationContext क्लास है, यह अदृश्य आंतरिक नलसाजी का उपयोग करता है। वे अपने प्रारंभिक प्रदाताओं को उनके प्रारंभिकरण में एक उदाहरण बनाते हैं और इसे सिंक्रनाइज़ेशन कॉन्टेक्स्ट.क्यूरेंट

कंसोल मोड ऐप के लिए ऐसा कोई प्रदाता नहीं है। मुख्य रूप से क्योंकि मुख्य धागा पूरी तरह से अनुपयुक्त है, यह एक प्रेषक पाश का उपयोग नहीं करता है। आप अपना खुद का निर्माण करना चाहते थे, फिर अपनी खुद की सिंक्रनाइज़ेशन कॉन्टेक्स्ट व्युत्पन्न कक्षा भी बनाएं। करना मुश्किल है, आप Console.ReadLine() जैसे कॉल नहीं कर सकते हैं क्योंकि यह पूरी तरह से विंडोज कॉल पर मुख्य थ्रेड को फ्रीज करता है। आपका कंसोल मोड ऐप एक कंसोल ऐप होने से रोकता है, यह Winforms ऐप जैसा दिखने लगेगा।

ध्यान दें कि इन रनटाइम वातावरण में एक अच्छे कारण के लिए सिंक्रनाइज़ेशन प्रदाता हैं। वे में है क्योंकि एक जीयूआई मौलिक रूप से थ्रेड-असुरक्षित है। कंसोल के साथ कोई समस्या नहीं है, यह थ्रेड-सुरक्षित है।

+0

डब्ल्यूसीएफ में समकक्ष सिंक्रनाइज़ेशन प्रदाता है? – theburningmonk

+0

सं। –

20

आप सकता है उपयोग CallContext.LogicalSetData और CallContext.LogicalGetData, लेकिन मैं तुम नहीं क्योंकि वे "क्लोनिंग" के किसी भी प्रकार का समर्थन नहीं करते हैं जब आप सरल समानांतरवाद (Task.WhenAny/Task.WhenAll) का उपयोग करें।

मैंने - संगत "संदर्भ" के लिए an MSDN forum post में अधिक विस्तार से समझाया, मैंने UserVoice request खोला। खुद को बनाने के लिए ऐसा प्रतीत नहीं होता है। जॉन स्कीट के पास इस विषय पर good blog entry है।

तो, मैं आपको तर्क, लैम्ब्डा बंद करने, या स्थानीय उदाहरण के सदस्यों (this) के सदस्यों का उपयोग करने की सलाह देता हूं, जैसा मार्क ने वर्णित किया है।

और हाँ, OperationContext.Currentawait एस में संरक्षित है।

अद्यतन: .NET 4.5 कोड में Logical[Get|Set]Data का समर्थन करता है। विवरण on my blog

0

, इस thread

ThreadStaticAttribute के साथ चिह्नित क्षेत्रों पर

पर एक नज़र प्रारंभ होगा केवल एक बार होता है। आपके कोड में जब आईडी 11 के साथ नया धागा बनाया गया है तो एक नया गुप्त फ़ील्ड बनाया जाएगा, लेकिन यह "प्रारंभ करें" कार्य पर लौटने पर खाली/शून्य है, कार्य थ्रेड 11 (आपके प्रिंटआउट शो के रूप में) पर समाप्त हो जाएगा और इसलिए स्ट्रिंग खाली है।

आप स्लीप को कॉल करने से पहले "स्टार्ट" के अंदर एक स्थानीय क्षेत्र में गुप्त संग्रह करके अपनी समस्या का समाधान कर सकते हैं, फिर नींद से लौटने के बाद स्थानीय क्षेत्र से गुप्त को पुनर्स्थापित कर सकते हैं। आप "प्रतीक्षा टास्क" डेले (1000) "कॉल करने से ठीक पहले स्लीप में भी कर सकते हैं;" जो वास्तव में थ्रेड स्विच का कारण बनता है।

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