Pthread_cond_wait के पास क्यों अजीब जगहें हैं?


145

मैन पेज को उद्धृत करने के लिए:

हालत चर का उपयोग करते समय हमेशा एक बूलियन विधेय होता है जिसमें प्रत्येक स्थिति से जुड़े साझा चर शामिल होते हैं जो कि थ्रेड के आगे बढ़ने के लिए सही है। Pthread_cond_timedwait () या pthread_cond_wait () फ़ंक्शन से गंभीर वेकअप हो सकते हैं। चूंकि preadread_cond_timedwait () या pthread_cond_wait () से वापसी इस predicate के मूल्य के बारे में कुछ भी नहीं बताती है, ऐसे रिटर्न पर predicate का पुनर्मूल्यांकन किया जाना चाहिए।

pthread_cond_waitयदि आप इसे संकेत नहीं दिया है , तो भी, वापस कर सकते हैं। पहली नज़र में कम से कम, कि बहुत नृशंस लगता है। यह एक फ़ंक्शन की तरह होगा जो गलत मूल्य को बेतरतीब ढंग से लौटाता है या बेतरतीब ढंग से वापस लौटता है क्योंकि यह वास्तव में एक उचित रिटर्न स्टेटमेंट तक पहुंच गया है। यह एक प्रमुख बग की तरह लगता है। लेकिन तथ्य यह है कि वे इसे ठीक करने के बजाय मैन पेज में दस्तावेज़ करने के लिए चुना है, यह इंगित करने के लिए प्रतीत होता है कि वहाँ एक वैध कारण है कि pthread_cond_waitअंत में स्वाभाविक रूप से जागने क्यों है। मुमकिन है, वहाँ यह कैसे काम करता है कि यह बनाता है ताकि है कि मदद नहीं किया जा सकता है के बारे में कुछ आंतरिक है। सवाल यह है कि

क्यों करता है pthread_cond_waitनकली तौर पर वापसी? यह गारंटी क्यों नहीं दे सकता है कि यह केवल तभी जागा होगा जब इसे ठीक से संकेत दिया गया हो? क्या कोई इसके सहज व्यवहार का कारण बता सकता है?


5
मैं कल्पना करता यह लौटने के लिए जब भी प्रक्रिया एक संकेत पकड़ता के साथ कुछ है। सिग्नल के बाधित होने के बाद अधिकांश * निक्स एक अवरोधक कॉल को पुनरारंभ नहीं करते हैं; वे बस एक त्रुटि कोड सेट / वापस करते हैं जो कहता है कि एक सिग्नल हुआ।
cHao

1
@cHao: हालाँकि ध्यान दें कि चूँकि कंडीशन वेरिएबल्स में किसी भी तरह से स्पार्क -वेक-अप के अन्य कारण होते हैं, सिग्नल को हैंडल करना इसके लिए कोई त्रुटि नहीं है pthread_cond_(timed)wait: "यदि कोई सिग्नल डिलीवर होता है ... थ्रेड फिर से शुरू हो जाता है जैसे कि कंडीशन वैरिएबल का इंतजार कर रहा है बाधित नहीं किया गया, या यह स्पुरियस वेकअप के कारण शून्य वापस आ जाएगा "। अन्य अवरुद्ध कार्य EINTRएक संकेत (जैसे read) से बाधित होने पर इंगित करते हैं, या फिर से शुरू करने के लिए आवश्यक होते हैं (जैसे pthread_mutex_lock)। अतः यदि स्फ़ूर्त जागने के कोई अन्य कारण नहीं थे pthread_cond_wait, तो उन दोनों की तरह परिभाषित किया जा सकता था।
स्टीव जेसोप

4
विकिपीडिया पर इससे संबंधित एक लेख: नकली Wakeup
Palec


कई फ़ंक्शन पूरी तरह से अपना काम पूरी तरह से नहीं कर सकते हैं (I / O से बाधित) और अवलोकन करने वाले फ़ंक्शन एक निर्देशिका में परिवर्तन की तरह गैर ईवेंट प्राप्त कर सकते हैं, जहां परिवर्तन रद्द कर दिया गया था या वापस लौटा दिया गया था। समस्या क्या है?
जिज्ञासु

जवाबों:


77

निम्नलिखित विवरण डेविड आर। बुटेनहोफ ने "पॉसिक्स थ्रेड्स के साथ प्रोग्रामिंग" (पृष्ठ 80) द्वारा दिया है ।

अजीब वेकअप अजीब लग सकता है, लेकिन कुछ मल्टीप्रोसेसर सिस्टम पर, स्थिति वेकअप को पूरी तरह से अनुमान लगाने योग्य हो सकता है, जो सभी वेरिएबल वेरिएबल ऑपरेशन को काफी धीमा कर सकता है।

निम्नलिखित में comp.programming.threads चर्चा , वह डिजाइन के पीछे की सोच पर फैलता:

पैट्रिक डॉयल ने लिखा: 
> लेख में, टॉम पायने ने लिखा है: 
>> Kaz Kylheku लिखा है: 
>>: ऐसा इसलिए है क्योंकि कार्यान्वयन कभी-कभी डालने से बच नहीं सकते हैं 
>>: इन शानदार वेकअप; यह उन्हें रोकने के लिए महंगा हो सकता है।

>> लेकिन क्यों? यह इतना कठीन क्यों है? उदाहरण के लिए, क्या हम बात कर रहे हैं
>> एक संकेत के रूप में एक प्रतीक्षा समय बाहर स्थितियों जहां आता है? 

> तुम्हें पता है, मुझे आश्चर्य है कि अगर pthreads के डिजाइनरों ने इस तरह से तर्क का इस्तेमाल किया: 
> हालत चर के उपयोगकर्ताओं को वैसे भी बाहर निकलने पर स्थिति की जांच करनी होगी, 
> इसलिए अगर हम अनुमति देते हैं तो हम उन पर कोई अतिरिक्त बोझ नहीं डालेंगे 
> स्पिरिट वेकअप; और चूंकि यह बोधगम्य है जो सहज होने की अनुमति देता है
> वेकअप तेजी से एक कार्यान्वयन कर सकता है, यह केवल तभी मदद कर सकता है जब हम 
> उन्हें अनुमति दें। 

> हो सकता है कि उनके मन में कोई विशेष कार्यान्वयन न हुआ हो। 

आप वास्तव में बहुत दूर नहीं हैं, सिवाय इसके कि आपने इसे बहुत दूर नहीं धकेला। 

आशय विधेय छोरों की आवश्यकता द्वारा सही / मजबूत कोड मजबूर करने के लिए किया गया था। यह था
में "मुख्य सेनाओं" के बीच सही ढंग से सही शैक्षणिक आकस्मिक द्वारा संचालित 
कार्यदल, हालांकि मुझे नहीं लगता कि कोई भी वास्तव में इरादे से असहमत था 
एक बार वे समझ गए कि इसका क्या मतलब है। 

हमने औचित्य के कई स्तरों के साथ उस इरादे का पालन किया। पहला वह था
"धार्मिक रूप से" एक लूप का उपयोग करके अपने स्वयं के अपूर्ण के खिलाफ आवेदन की सुरक्षा करता है 
कोडिंग प्रैक्टिस। दूसरा यह था कि कल्पना करना मुश्किल नहीं था
मशीनें और कार्यान्वयन कोड जो इस आवश्यकता को बेहतर बनाने के लिए शोषण कर सकते हैं 
के अनुकूलन के माध्यम से औसत स्थिति प्रतीक्षा संचालन का प्रदर्शन 
तुल्यकालन तंत्र। 
/ ------------------ [David.Buten ... @ compaq.com] ------------------ \ 
| कॉम्पैक कंप्यूटर कॉर्पोरेशन POSIX थ्रेड आर्किटेक्ट |
| मेरी पुस्तक: http://www.awl.com/cseng/tmarks/0-201-63392-2/ |
\ ----- [http://home.earthlink.net/~anneart/family/dave.html] ----- / 


22
मूल रूप से यह कुछ भी नहीं कहता है। प्रारंभिक विचार के अलावा यहां कोई स्पष्टीकरण नहीं दिया गया है कि "यह चीजों को तेज कर सकता है", लेकिन किसी को नहीं पता कि यह कैसे या क्या करता है।
बोगदान आयनिट्जा

107

कम से कम दो चीजें हो सकती हैं 'स्पिरिटस वेकअप' का मतलब हो सकता है:

  • pthread_cond_waitकॉल में अवरुद्ध एक धागा तब भी वापस आ सकता है, जबकि कोई भी कॉल pthread_call_signalया pthread_cond_broadcastशर्त पर नहीं हुई थी।
  • एक pthread_cond_waitकॉल के कारण pthread_cond_signalया pthread_cond_broadcastफिर रिटर्न में अवरुद्ध एक धागा , हालांकि म्यूटेक्स को पुनः प्राप्त करने के बाद अंतर्निहित विधेय को अब सच नहीं पाया जाता है।

लेकिन बाद का मामला तब भी हो सकता है जब शर्त चर कार्यान्वयन पूर्व मामले की अनुमति नहीं देता है। एक निर्माता के उपभोक्ता कतार, और तीन सूत्र पर विचार करें।

  • थ्रेड 1 ने केवल एक तत्व को हटा दिया है और म्यूटेक्स जारी किया है, और कतार अब खाली है। थ्रेड जो कुछ भी कर रहा है वह कुछ सीपीयू पर प्राप्त तत्व के साथ करता है।
  • थ्रेड 2 एक तत्व को समाप्त करने का प्रयास करता है, लेकिन pthread_cond_waitसिग्नल / प्रसारण की प्रतीक्षा में म्यूटेक्स, कॉल और ब्लॉक के तहत चेक किए जाने पर कतार खाली पाई जाती है ।
  • थ्रेड 3 म्यूटेक्स प्राप्त करता है, कतार में एक नया तत्व सम्मिलित करता है, स्थिति चर को सूचित करता है, और लॉक को रिलीज़ करता है।
  • थ्रेड 3 से अधिसूचना के जवाब में, थ्रेड 2, जो स्थिति पर इंतजार कर रहा था, को चलाने के लिए निर्धारित है।
  • हालाँकि, थ्रेड से पहले 2 सीपीयू पर जाता है और कतार लॉक को पकड़ लेता है, थ्रेड 1 अपने वर्तमान कार्य को पूरा करता है, और अधिक काम के लिए कतार में लौटता है। यह कतार लॉक प्राप्त करता है, विधेय की जांच करता है, और पाता है कि कतार में काम है। यह उस आइटम को समाप्त करने के लिए आगे बढ़ता है जो थ्रेड 3 डाला गया है, लॉक को रिलीज़ करता है, और जो भी करता है वह उस आइटम के साथ होता है जो थ्रेड 3 संलग्न है।
  • थ्रेड 2 अब एक सीपीयू पर जाता है और लॉक प्राप्त करता है, लेकिन जब यह विधेय की जांच करता है, तो यह पाता है कि कतार खाली है। थ्रेड 1 'आइटम' चुराता है, इसलिए वेक्यूर स्पुरियस प्रतीत होता है। थ्रेड 2 को फिर से स्थिति पर प्रतीक्षा करने की आवश्यकता है।

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


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

2
"स्प्यूरियस वेकप" का अधिक सामान्य परिदृश्य सबसे अधिक संभावना pthread_cond_broadcast () के कॉल का साइड-इफ़ेक्ट है। मान लीजिए कि आपके पास 5 धागों का एक पूल है, दो प्रसारण के लिए उठते हैं और काम करते हैं। अन्य तीन जागते हैं और पाते हैं कि काम हो गया है। मल्टी-प्रोसेसर सिस्टम भी एक सशर्त सिग्नल का परिणाम हो सकता है जिससे दुर्घटना के कई सूत्र जागते हैं। कोड बस फिर से विधेय की जांच करता है, एक अमान्य स्थिति देखता है, और सो जाता है। या तो मामले में, विधेय की जाँच करने से समस्या हल हो जाती है। IMO, सामान्य रूप से, उपयोगकर्ताओं को कच्चे POSIX म्यूटेक्स और सशर्त का उपयोग नहीं करना चाहिए।
क्यूबिकलसॉफ्ट

1
@MartinJames - क्लासिक "सहज" EINTR के बारे में कैसे? मैं इस बात से सहमत हूँ कि एक लूप में EINTR के लिए लगातार परीक्षण एक परेशान करने वाला कोड है और कोड को बदसूरत बनाता है लेकिन डेवलपर्स इसे बेतरतीब टूटने से बचने के लिए वैसे भी करते हैं।
क्यूबिकलसॉफ्ट

2
@ योला यह नहीं हो सकता है, क्योंकि आप म्यूटेक्स को चारों ओर से बंद करने वाले हैं pthread_cond_signal/broadcastऔर आप ऐसा नहीं कर पाएंगे, जब तक कि म्यूटेक्स को कॉल करके अनलॉक नहीं किया जाता है pthread_cond_wait
19

1
इस उत्तर का उदाहरण बहुत यथार्थवादी है और मैं मानता हूं कि विधेय की जाँच करना एक अच्छा विचार है। हालाँकि, इसे समसामयिक चरण "थ्रेड 1 अपने वर्तमान कार्य को पूरा करने, और अधिक काम के लिए कतार में लौटकर" के रूप में समान रूप से समान रूप से तय नहीं किया जा सकता था और इसे "थ्रेड 1" के साथ बदलकर अपने वर्तमान कार्य को पूरा करता है, और प्रतीक्षा पर वापस जाता है हालत चर "? यह उत्तर में वर्णित विफलता मोड को समाप्त कर देगा, और मुझे पूरा यकीन है कि यह स्पार्टियस वेकअप्स की अनुपस्थिति में कोड को सही बना देगा । क्या कोई वास्तविक क्रियान्वयन है जो अभ्यास में स्पष्ट रूप से जागरण पैदा करता है?
क्क्सप्लसोन

7

Pthread_cond_signal में सेक्शन "मल्टीपल अवनिंग्स बाय कंडिशन सिग्नल" में pthread_cond_wait और pthread_cond_signal का एक उदाहरण कार्यान्वयन है, जिसमें शानदार वेकअप शामिल हैं।


2
मुझे लगता है कि यह उत्तर गलत है, जहां तक ​​यह जाता है। उस पृष्ठ पर नमूना कार्यान्वयन में "एक सूचित करें" का कार्यान्वयन है जो "सभी को सूचित करें" के बराबर है; लेकिन यह वास्तव में उत्पन्न करने के लिए प्रतीत नहीं होता नकली wakeups। एक धागे के लिए जागने का एकमात्र तरीका किसी अन्य धागे द्वारा "सभी को सूचित करें", या किसी अन्य धागे को-चीज़-लेबल को लागू करने से होता है- "एक को सूचित करें-जो वास्तव में है-" सभी को सूचित करें "।
क्क्सप्लसोन

5

हालांकि मुझे नहीं लगता कि यह डिजाइन के समय पर विचार किया गया था, यहां एक वास्तविक तकनीकी कारण है: थ्रेड कैंसिलेशन के साथ, ऐसी स्थितियां हैं जिनके तहत "स्फ़र" जागने का विकल्प लेना बिल्कुल आवश्यक हो सकता है, कम से कम जब तक आप कार्यान्वयन रणनीतियों के किस प्रकार संभव हैं पर बहुत मजबूत बाधाओं को लागू करने के लिए तैयार हैं।

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

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.