2017-02-26 21 views
5

मैं std::condition_variable(lock,pred) के कुलपति ++ कार्यान्वयन देखा है, मूल रूप से, यह इस तरह दिखता है:std :: condition_variable में संभावित दौड़ की स्थिति?

template<class _Predicate> 
     void wait(unique_lock<mutex>& _Lck, _Predicate _Pred) 
     { // wait for signal and test predicate 
     while (!_Pred()) 
      wait(_Lck); 
     } 

असल में, नग्न wait कॉल _Cnd_waitX जो _Cnd_wait जो do_wait जो cond->_get_cv()->wait(cs); (कॉल कॉल कॉल इन सब में हैं फ़ाइल cond.c)।

cond->_get_cv()Concurrency::details::stl_condition_variable_interface देता है।

यदि हम फ़ाइल primitives.h पर जाते हैं, हम देखते हैं कि विंडोज 7 और इसके बाद के संस्करण के अंतर्गत, हम वर्ग stl_condition_variable_win7 जो पुराने अच्छा Win32 CONDITION_VARIABLE, और wait कॉल __crtSleepConditionVariableSRW शामिल है।

कुछ असेंबली डीबग करना, __crtSleepConditionVariableSRW बस SleepConditionVariableSRW फ़ंक्शन पॉइंटर निकालें, और इसे कॉल करें।

यहां तक ​​कि बात है: जहां तक ​​मुझे पता है, Win32 CONDITION_VARIABLE कर्नेल ऑब्जेक्ट नहीं है, लेकिन उपयोगकर्ता मोड एक है। इसलिए, यदि कुछ धागे इस चर को सूचित करते हैं और कोई धागा वास्तव में उस पर सो नहीं जाता है, तो आप अधिसूचना खो देते हैं, और थ्रेड समय तक पहुंचने तक थ्रेड रहेगा या कुछ अन्य धागे इसे सूचित करते हैं। एक छोटा कार्यक्रम वास्तव में इसे साबित कर सकता है - यदि आप अधिसूचना के बिंदु को याद करते हैं - आपका धागा सो रहेगा हालांकि कुछ अन्य धागे ने इसे अधिसूचित किया है।

मेरा प्रश्न इस तरह है:
एक थ्रेड एक शर्त चर पर इंतजार करता है और भविष्यवाणी झूठी रिटर्न देता है। फिर, ऊपर वर्णित कॉल की पूरी श्रृंखला जगह लेता है। उस समय, एक और थ्रेड ने पर्यावरण को बदल दिया ताकि भविष्यवाणी सही हो जाएगी और हालत चर को सूचित करता है। हमने मूल धागे में भविष्यवाणी की, लेकिन हम अभी भी SleepConditionVariableSRW में नहीं गए - कॉल श्रृंखला बहुत लंबी है।

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

क्या यह व्यवहार करना चाहिए? ऐसा लगता है कि एक बड़ी बदसूरत दौड़ की स्थिति होने की प्रतीक्षा है। यदि आप एक शर्त चर को सूचित करते हैं और यह अनुमानित सत्य सत्य देता है - थ्रेड को अनब्लॉक करना चाहिए। लेकिन अगर हम भविष्यवाणी की जांच करने और सोने के लिए लिम्बो में हैं - हम हमेशा के लिए अवरुद्ध हैं। std::condition_variable::wait एक परमाणु कार्य नहीं है।

मानक इसके बारे में क्या कहता है और क्या यह वास्तव में दौड़ की स्थिति है?

उत्तर

2

आपने अनुबंध का उल्लंघन किया है ताकि सभी दांव बंद हो जाएं। देखें: http://en.cppreference.com/w/cpp/thread/condition_variable

टीएलडीआर: म्यूटेक्स धारण करते समय किसी और द्वारा भविष्यवाणी करना असंभव है।

आप एक म्युटेक्स पकड़े और जब तुम std::condition_variable::wait कॉल करने से पहले कि म्युटेक्स प्राप्त करने के लिए (दोनों क्योंकि wait विज्ञप्ति म्युटेक्स, और क्योंकि है कि अनुबंध है) विधेय के अंतर्निहित चर बदलने के लिए चाहिए रहे हैं।

परिदृश्य में आप वर्णित परिवर्तन हुआ के बाद while (!_Pred()) देखा कि विधेय नहीं रखता है लेकिन wait(_Lck) से पहले म्युटेक्स जारी करने के लिए एक मौका था। इसका मतलब है कि आपने म्यूटेक्स को पकड़े बिना अनुमानित जांच को बदल दिया है। आपने नियमों और दौड़ की स्थिति का उल्लंघन किया है या अनंत प्रतीक्षा अभी भी यूबी के सबसे खराब प्रकार नहीं हैं। कम से कम इन स्थानीय और नियमों का उल्लंघन किया है आप ताकि आप त्रुटि पा सकते हैं करने के लिए संबंधित हैं ...

आप, नियमों से खेलते हैं या तो:

  1. वेटर म्युटेक्स पहले
  2. को पकड़ लेता है
  3. std::condition_variable::wait में जाता है। (नोटिफ़ायर को अभी भी म्यूटेक्स पर प्रतीक्षा करें।)
  4. भविष्यवाणी करता है और देखता है कि यह नहीं है। (नोटिफ़ायर को याद रखें अभी भी म्यूटेक्स पर प्रतीक्षा करता है।)
  5. म्यूटेक्स को छोड़ने और प्रतीक्षा करने के लिए कुछ कार्यान्वयन परिभाषित जादू को कॉल करें, और केवल नोटिफ़ायर आगे बढ़ सकता है।
  6. नोटिफ़ायर अंततः म्यूटेक्स लेने में कामयाब रहा।
  7. नोटिफ़ायर परिवर्तन को भविष्य में बदलने के लिए जो कुछ भी बदलने की जरूरत है उसे बदलता है।
  8. नोटिफ़ायर std::condition_variable::notify_one पर कॉल करता है।

या:

  1. सूचक म्युटेक्स प्राप्त कर लेता है। (याद रखें कि म्यूटेक्स प्राप्त करने की कोशिश करने पर वेटर अवरुद्ध है।)
  2. नोटिफ़ायर को भविष्य में बदलने के लिए जो कुछ भी बदलने की आवश्यकता है उसे बदलता है। (याद रखें कि वेटर अभी भी अवरुद्ध है।)
  3. नोटिफ़ायर म्यूटेक्स जारी करता है। (कहीं भी वेटर std::condition_variable::notify_one पर कॉल करेगा, लेकिन एक बार म्यूटेक्स जारी हो जाने पर ...)
  4. वेटर म्यूटेक्स प्राप्त करता है।
  5. वेटर std::condition_variable::wait पर कॉल करता है।
  6. वेटर while (!_Pred()) और व्हायोला चेक करता है! भविष्यवाणी सच है।
  7. वेटर आंतरिक wait में भी नहीं जाता है, इसलिए नोटिफ़ायर std::condition_variable::notify_one पर कॉल करने में कामयाब रहा या अभी तक ऐसा करने में सक्षम नहीं है, यह अप्रासंगिक है।

cppreference.com पर आवश्यकता के पीछे तर्क है कि:

यहां तक ​​कि अगर साझा चर परमाणु है, यह म्युटेक्स के तहत आदेश सही ढंग से इंतज़ार कर रहे धागा करने के लिए संशोधन प्रकाशित करने के लिए संशोधित किया जाना चाहिए।

ध्यान दें कि यह std::condition_variables रों (सहित विंडोज CONDITION_VARIABLE रों, POSIX pthread_cond_t रों, आदि) के लिए एक विशेष आवश्यकताओं हालत चर के बजाय के लिए एक सामान्य नियम है।


याद रखें कि wait अधिभार कि एक विधेय लेता है सिर्फ एक सुविधा समारोह है ताकि फोन करने वाले नकली wakeups से निपटने के लिए नहीं है। मानक (§30.5.1/15) स्पष्ट रूप से कहा गया है कि इस अधिभार माइक्रोसॉफ्ट के कार्यान्वयन में जबकि पाश के बराबर है:

प्रभाव: समतुल्य:

while (!pred()) 
    wait(lock); 

सरल करता है wait काम? क्या आप wait पर कॉल करने से पहले और बाद में भविष्यवाणी का परीक्षण करते हैं? महान। तुम वही कर रहे हो या आप void std::condition_variable::wait(std::unique_lock<std::mutex>& lock); पर भी सवाल कर रहे हैं?


विंडोज क्रिटिकल अनुभागों और स्लिम रीडर/लेखक ताले उपयोगकर्ता के मोड सुविधाओं के बजाय गिरी वस्तुओं जा रहा है सारहीन और सवाल के लिए अप्रासंगिक है। वैकल्पिक कार्यान्वयन हैं। यदि आप जानना चाहते हैं कि विंडोज परमाणु रूप से सीएस/एसआरडब्लूएल को कैसे जारी किया जाता है और एक प्रतीक्षा स्थिति दर्ज करें (म्यूटेक्स और घटनाओं के साथ पूर्व-विस्टा उपयोगकर्ता-मोड कार्यान्वयन क्या गलत है) यह एक अलग सवाल है।

+0

आह, मुझे याद आया "यहां तक ​​कि अगर साझा चर परमाणु है ..." जो मुझे दिमाग में था। –

+0

@ डेविडहाइम: ध्यान दें कि यह cppreference द्वारा दिया गया एक संकेत है। मानक इस तरह के कुछ भी नहीं कहता है। 'std :: condition_variable' (साथ ही सामान्य रूप से हालत चर) प्रतीक्षा और अधिसूचनाओं के बीच एक रिश्ता बनाता है और एक म्यूटेक्स धारण करता है। यह माना जाता है कि इस तरह से एक mutex प्राप्त करने के लिए आपके पास वैध कारण है। या नहीं। उस म्यूटेक्स के साथ क्या करना है आपका व्यवसाय है। एकमात्र चीज हालत चर वास्तव में एक म्यूटेक्स जारी करता है और एक प्रतीक्षा स्थिति * परमाणु * दर्ज करें। "हालत" भाग पूरी तरह से आपके ऊपर है। – conio

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