2012-01-04 19 views
19

मान लें मैं एक क्षेत्र है कि कुछ पाश के निष्पादन को नियंत्रित करता है:क्या यह Thread.MemoryBarrier() का सही उपयोग है?

private static bool shouldRun = true; 

और मैं एक धागा चल रहा है, की तरह कोड है कि है:

while(shouldRun) 
{ 
    // Do some work .... 
    Thread.MemoryBarrier(); 
} 

अब, एक और धागा false करने के लिए shouldRun सेट कर सकते हैं, किसी भी सिंक्रनाइज़ेशन तंत्र का उपयोग किए बिना।

जहां तक ​​मैं थ्रेड को समझता हूं। मेमरीबैरियर(), जबकि लूप के अंदर यह कॉल shouldRun के कैश किए गए संस्करण को प्राप्त करने से रोकता है, और प्रभावी रूप से एक अनंत लूप को होने से रोकता है।

क्या थ्रेड के बारे में मेरी समझ है। मेमरीबैरियर सही है? यह देखते हुए कि मेरे पास धागे हैं जो shouldRun चर सेट कर सकते हैं (इसे आसानी से बदला नहीं जा सकता है), क्या यह सुनिश्चित करने का एक उचित तरीका है कि मेरा लूप shouldRun किसी भी थ्रेड द्वारा गलत पर सेट हो जाएगा?

+0

बूलियन के रूप में वाष्पशील करते घोषित नहीं करता है वहाँ वास्तव में सिर्फ इरादा धागा तुल्यकालन वस्तुओं का उपयोग कर के साथ कुछ भी गलत नहीं है वही चीज़? मुझे थ्रेड के बारे में निश्चित नहीं है। मेमरीबैरियर इसलिए टिप्पणी का जवाब नहीं है। जैसे http://www.albahari.com/threading/part4.aspx अन्य> "इस समस्या को हल करने का एक और अधिक उन्नत तरीका अस्थिर कीवर्ड को अपूर्ण फ़ील्ड पर लागू करना है। अस्थिर कीवर्ड प्रत्येक पर एक अधिग्रहण-बाड़ उत्पन्न करने के लिए संकलक को निर्देश देता है उस क्षेत्र से पढ़ें, और उस क्षेत्र में प्रत्येक लिखने पर एक रिलीज-बाड़ "। विनम्र, –

+3

अस्थिरता एक बहुत ही बुरी चीज है और इसका उपयोग नहीं किया जाना चाहिए: http://blogs.msdn.com/b/ericlippert/archive/2011/06/16/atomicity-volatility-and-immutability-are- अलग-part -three.aspx – Steven

+1

लॉल, मुझे इसके बारे में पता नहीं था! एक पूर्व सी/एएसएम प्रोग्रामर के रूप में मैंने सोचा कि यह एक अच्छी बात थी :) लिंक के लिए धन्यवाद, मैं एक नज़र डालेगा। –

उत्तर

26

है द्वारा पढ़ा जाता है के सभी मजबूर कर रहा है है यह Thread.MemoryBarrier() का सही उपयोग है?

नंबर एक धागा झंडा से पहले पाश भी निष्पादित करने के लिए शुरू होता है सेट मान लीजिए। फ्लैग के कैश किए गए मान का उपयोग करके लूप अभी भी एक बार निष्पादित कर सकता है। क्या यह सही है? यह निश्चित रूप से मेरे लिए गलत लगता है। मैं उम्मीद करता हूं कि अगर मैंने लूप के पहले निष्पादन से पहले ध्वज सेट किया है, तो लूप शून्य बार निष्पादित करता है, एक बार नहीं।

जहां तक ​​मेरा Thread.MemoryBarrier() को समझते हैं, जबकि पाश अंदर इस कॉल shouldRun का संचित संस्करण हो रही है, और प्रभावी ढंग से हो रहा से अनंत लूप को रोकने से मेरे काम धागा पाएगा रही है। क्या Thread.MemoryBarrier के बारे में मेरी समझ सही है?

स्मृति बाधा यह सुनिश्चित करेंगे कि प्रोसेसर किसी भी reorderings नहीं करता है पढ़ता है और बाद यह इस तरह है कि एक स्मृति का उपयोग तार्किक है कि से पहले बाधा वास्तव में माना गया है लिखते हैं की, और इसके विपरीत।

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

यह सुनिश्चित करने का एक उचित तरीका है कि मेरा लूप किसी भी थ्रेड द्वारा गलत होने पर रोक दिया जाए?

कुछ लोग इसे उचित मानेंगे। मैं इसे अपने कोड में बहुत, बहुत अच्छे कारण के बिना नहीं करूँगा।

आमतौर पर कम लॉक तकनीक प्रदर्शन विचारों द्वारा उचित होती है। ऐसे दो विचार हैं:

सबसे पहले, एक contended ताला संभावित रूप से बेहद धीमी है; जब तक लॉक में कोड निष्पादित होता है तब तक यह अवरुद्ध होता है। यदि आपके पास प्रदर्शन समस्या है क्योंकि बहुत अधिक विवाद है तो मैं पहले विवाद को समाप्त कर समस्या को हल करने का प्रयास करूंगा। केवल अगर मैं विवाद को खत्म नहीं कर सका तो मैं कम लॉक तकनीक पर जाऊंगा।

दूसरा, यह हो सकता है कि असंबद्ध लॉक बहुत धीमा है। यदि लूप में आप "काम" कर रहे हैं, तो कहें, 200 नैनोसेकंड कम करें, तब अनचाहे लॉक की जांच करने के लिए आवश्यक समय - लगभग 20 एनएस - काम करने में व्यतीत समय का एक महत्वपूर्ण अंश है। उस स्थिति में मैं सुझाव दूंगा कि आप प्रति लूप पर अधिक काम करें। क्या यह वास्तव में जरूरी है कि लूप नियंत्रण ध्वज के 200 एनएस के भीतर बंद हो जाए?

केवल प्रदर्शन परिदृश्यों के सबसे चरम में मुझे लगता है कि एक असंतुलित ताला की जांच करने की लागत कार्यक्रम में बिताए गए समय का एक महत्वपूर्ण अंश है।

और निश्चित रूप से, यदि आप हर 200 एनएस या तो मेमोरी बाधा उत्पन्न कर रहे हैं, तो आप संभवतः अन्य तरीकों से प्रदर्शन को तोड़ रहे हैं। प्रोसेसर चाहता है ताकि आपके लिए उन चलती-मेमोरी-एक्सेस-इन-इन-टाइम ऑप्टिमाइज़ेशन को बनाया जा सके; यदि आप लगातार उन अनुकूलन को त्यागने के लिए मजबूर कर रहे हैं, तो आप संभावित जीत पर छूट रहे हैं।

2

इस मामले Thread.MemoryBarrier(); में

private static volatile readonly bool shouldRun = true; 

के एक कम performant, लेकिन नहीं कम सुरक्षित संस्करण है, तो इस लक्ष्य को हासिल करने के लिए आवश्यक है, क्योंकि अस्थिर रूप shouldRun की घोषणा एक स्मृति बाधा प्रदर्शन करेंगे एक अप-टू-डेट पढ़ें, लेकिन ऐसा करने की आवश्यकता नहीं हो सकती है।

3

आप जो करने की कोशिश कर रहे हैं उसके आधार पर, यह शायद आप जो भी उम्मीद करते हैं वह नहीं करेगा। MSDN से, MemoryBarrier:

इस प्रकार स्मृति पहुँच सिंक्रनाइज़: प्रोसेसर वर्तमान धागा क्रियान्वित इस तरह से निर्देश को पुन: व्यवस्थित नहीं कर सकता कि स्मृति पहले तक पहुँचता करने MemoryBarrier करने के लिए कॉल स्मृति के बाद निष्पादित कि कॉल का पालन accesses मेमोरीबैरियर के लिए।

यह यह पहले के लिए स्मृति बाधा कॉल के बाद निर्देश की पुनर्व्यवस्था कर पाएगा, लेकिन यह निर्णय लेने से है कि पाश एक और होना चाहिए से धागा शेड्यूलिंग पर रोक नहीं लगेगी चारों ओर घूमते से पहले लेखक धागा वास्तव में लिखने, उदा करता है यह लॉकिंग या सिंक्रनाइज़ेशन तंत्र नहीं है। यह से पहले से पहले क्रमशः 0Rको फिर से आदेशित करने से लूप (जैसे वैरिएबल प्रारंभिकरण) में अन्य निर्देशों को रोकता है।

इसी प्रकार, इसे छोड़कर एक अनंत लूप नहीं होगा- किसी भी मामले में, प्रत्येक पुनरावृत्ति के साथ चाहिए। यहां कोई "कैश वैल्यू" नहीं है।

+0

जब तक थ्रेड को देखना गलत नहीं होता है, तब तक कोई विशिष्ट सिंक्रनाइज़ेशन आवश्यक नहीं होता है, तो किसी भी बाड़ लगाने की आवश्यकता नहीं होती है। फील्ड मूल्य अंततः बदलने के लिए मनाया जाएगा; बाड़ लगाना केवल एक मुद्दा है यदि सही व्यवहार होने के लिए पढ़ने और लिखने की एक विशेष आवश्यक आदेश/अवलोकनता है। –

7

मेरा मानना ​​है कि अपनी समझ को इस लाइन पर एक सा बंद है

जहां तक ​​मैं समझता हूँ Thread.MemoryBarrier() है, जबकि पाश अंदर इस कॉल होने का संचित संस्करण को प्राप्त करने से मेरे काम धागा पाएगा कंधे, और प्रभावी रूप से एक अनंत लूप को रोकने से रोक रहा है।

एक मेमोरी बाधा पढ़ने/लिखने के निर्देशों पर ऑर्डरिंग बाधा लागू करने का एक तरीका है। जबकि पढ़ने/लिखने के पुनरीक्षण के परिणाम में स्मृति बाधा को कैशिंग करने की उपस्थिति हो सकती है, वास्तव में किसी भी तरह से कैशिंग को प्रभावित नहीं करती है।यह बस एक बाड़ के रूप में कार्य करता है जिस पर पढ़ना और लिखना निर्देश पार नहीं कर सकते हैं।

सभी संभावनाओं में यह एक अनंत लूप को नहीं रोकेगा। क्या स्मृति बाड़ कर रही है इस परिदृश्य रीड और राईट जो पाश shouldRun का मूल्य से पहले होने के शरीर में होती हैं पाश सशर्त

Wikipedia has a nice walk through on memory barrier that you may find useful

3

यह प्रश्न का सीधा जवाब नहीं है; हालांकि, यह उपर्युक्त पदों में अच्छी तरह से कवर किया गया प्रतीत होता है। स्पष्ट रूप से नहीं बताया गया है कि वांछित परिणाम कैसे प्राप्त करें।

private static readonly ManualResetEvent shutdownEvent = new ManualResetEvent(false); 

//Thread 1 - continue until event is signaled 
while(!shutodwnEvent.WaitOne(0)) 
{ 
    // Do some work .... 
    Thread.MemoryBarrier(); 
} 

//Thread 2 - signal the other thread 
shutdownEvent.Set(); 

अन्य दृष्टिकोण एक ताला बयान जब चर तक पहुँचने का उपयोग करना है:

private static object syncRoot = new Object(); 
private static bool shouldRun = true; 

//Thread 1 
bool bContinue; 
lock(syncRoot) 
    bContinue = shouldRun; 

while(bContinue) 
{ 
    // Do some work .... 
    lock(syncRoot) 
     bContinue = shouldRun; 
} 

//Thread 2 
lock(syncRoot) 
    shouldRun = false; 
संबंधित मुद्दे