क्यों pthreads 'स्थिति चर कार्यों म्यूटेक्स की आवश्यकता है?


182

मैं पढ़ रहा हूं pthread.h; स्थिति चर संबंधित कार्यों (जैसे pthread_cond_wait(3)) को एक तर्क के रूप में म्यूटेक्स की आवश्यकता होती है। क्यों? जहाँ तक मेरा बता सकते हैं, मैं एक म्युटेक्स बनाने जा रहा हूँ बस कि तर्क के रूप में उपयोग करने के लिए? उस म्यूटेक्स को क्या करना चाहिए?

जवाबों:


194

यह सिर्फ तरीका है कि हालत चर रहे हैं (या मूल रूप से) लागू किया गया।

म्यूटेक्स का उपयोग कंडीशन चर की सुरक्षा के लिए किया जाता है । इसलिए आपको प्रतीक्षा करने से पहले इसे बंद करने की आवश्यकता है।

प्रतीक्षा "atomically" म्यूटेक्स को अनलॉक करेगी, दूसरों को स्थिति चर (सिग्नलिंग के लिए) तक पहुंचने की अनुमति देगा। फिर जब स्थिति चर का संकेत दिया जाता है या प्रसारण किया जाता है, तो प्रतीक्षा सूची में एक या अधिक थ्रेड्स जागृत हो जाएंगे और म्यूटेक्स को उस धागे के लिए फिर से जादुई रूप से बंद कर दिया जाएगा।

आप आमतौर पर निम्न स्थिति को चर के साथ देखते हैं, यह दर्शाता है कि वे कैसे काम करते हैं। निम्नलिखित उदाहरण एक श्रमिक धागा है जिसे एक स्थिति चर के संकेत के माध्यम से काम दिया जाता है।

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            do the work.
    unlock mutex.
    clean up.
    exit thread.

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

उपरोक्त कोड एक एकल-उपभोक्ता मॉडल है क्योंकि म्यूटेक्स लॉक रहता है जबकि काम किया जा रहा है। एक बहु-उपभोक्ता भिन्नता के लिए, आप एक उदाहरण के रूप में उपयोग कर सकते हैं :

thread:
    initialise.
    lock mutex.
    while thread not told to stop working:
        wait on condvar using mutex.
        if work is available to be done:
            copy work to thread local storage.
            unlock mutex.
            do the work.
            lock mutex.
    unlock mutex.
    clean up.
    exit thread.

जो अन्य उपभोक्ताओं को काम करने की अनुमति देता है, जबकि यह काम कर रहा है।

स्थिति चर आपको कुछ स्थिति होने के बजाय किसी अन्य थ्रेड को सूचित करने की अनुमति देने के बजाय कुछ स्थिति के मतदान के बोझ से छुटकारा दिलाता है। एक अन्य सूत्र यह बता सकता है कि वह धागा जो इस प्रकार उपलब्ध है:

lock mutex.
flag work as available.
signal condition variable.
unlock mutex.

आमतौर पर गलत तरीके से कहे जाने वाले वेक्युप्स बहुसंख्यक हमेशा से थे क्योंकि उनके pthread_cond_waitकॉल (ब्रॉडकास्ट) में कई थ्रेड्स संकेतित किए गए थे , एक म्यूटेक्स के साथ वापस आ जाएगा, काम करेगा, फिर इंतजार करेगा।

तब दूसरा सिगनल थ्रेड बाहर आ सकता है जब कोई काम नहीं करना था। तो आपके पास एक अतिरिक्त चर होना चाहिए जो दर्शाता है कि काम किया जाना चाहिए (यह संक्षेप में म्यूटेक्स-म्यूटेक्स जोड़ी के साथ संरक्षित है - हालांकि इसे बदलने से पहले म्यूटेक्स को लॉक करने के लिए आवश्यक अन्य थ्रेड्स)।

यह था तकनीकी रूप से संभव के लिए एक धागा अन्य प्रक्रिया द्वारा लात मारी जा रहा है बिना एक शर्त प्रतीक्षा से वापस जाने के लिए (यह एक वास्तविक नकली Wakeup है), लेकिन, मेरे सभी कई वर्षों में pthreads पर काम कर रहा है, दोनों के विकास में / कोड की सेवा और एक उपयोगकर्ता के रूप में उनमें से, मुझे कभी भी इनमें से एक नहीं मिला। हो सकता है कि सिर्फ इसलिए कि एचपी एक सभ्य कार्यान्वयन था :-)

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


3
'कुछ मत करो' लूप के अंदर नहीं होना चाहिए। आप चाहते हैं कि आपका लूप सिर्फ़ हालत की जाँच करे, अन्यथा अगर आप एक स्पिरिट वेकअप करते हैं, तो आप भी कुछ कर सकते हैं।
ओपन स्कूल

1
नहीं, त्रुटि से निपटने के लिए यह दूसरा है। Pthreads के साथ, आप बिना किसी स्पष्ट कारण (एक अजीब वेकअप), और किसी भी त्रुटि के साथ जाग सकते हैं। इस प्रकार आपको जागने के बाद 'कुछ स्थिति' की जाँच करनी होगी।
ओपन स्कूल

1
मुझे यकीन नहीं कि मैं समझा हूँ। मेरे पास नोस के समान प्रतिक्रिया थी ; लूप के do somethingअंदर क्यों है while?
ELLIOTTCABLE

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

7
@stefaanv "म्यूटेक्स अभी भी स्थिति चर की रक्षा करने के लिए है, इसे बचाने के लिए कोई अन्य तरीका नहीं है": म्यूटेक्स शर्त चर की रक्षा करने के लिए नहीं है; यह विधेय डेटा की सुरक्षा के लिए है , लेकिन मुझे लगता है कि आपको पता है कि आपकी टिप्पणी को पढ़ने से उस कथन का पालन होता है। आप कानूनी रूप से एक स्थिति चर का संकेत दे सकते हैं, और कार्यान्वयन के द्वारा पूरी तरह से समर्थित है, म्यूटेक्स के पोस्ट -लॉक को विधेय को लपेटते हैं, और वास्तव में आप कुछ मामलों में ऐसा करने में विवाद को राहत देंगे ।
WhozCraig

59

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

pthreads भी आपको दे सकता है, बल्कि तकनीकी कारणों से, एक स्पुरियस वेकअप । इसका मतलब है कि आपको एक विधेय की जांच करने की आवश्यकता है, इसलिए आप यह सुनिश्चित कर सकते हैं कि वास्तव में इस स्थिति का संकेत दिया गया था - और यह कि एक स्प्यूरियस वेकअप से अलग। प्रतीक्षा करने के संबंध में ऐसी स्थिति की रक्षा करने की आवश्यकता है - इसलिए एक शर्त चर को उस स्थिति की रक्षा करने वाले म्यूटेक्स को लॉक / अनलॉक करते समय कम से कम प्रतीक्षा / जागने के लिए रास्ता चाहिए।

एक साधारण उदाहरण पर विचार करें जहां आपको सूचित किया जाता है कि कुछ डेटा का उत्पादन किया गया है। हो सकता है कि किसी अन्य थ्रेड ने कुछ डेटा बनाया जो आप चाहते हैं, और उस डेटा के लिए एक पॉइंटर सेट करें।

एक निर्माता के धागे को 'some_data' पॉइंटर के माध्यम से किसी अन्य उपभोक्ता थ्रेड को कुछ डेटा देने की कल्पना करें।

while(1) {
    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    char *data = some_data;
    some_data = NULL;
    handle(data);
}

आप स्वाभाविक रूप से बहुत दौड़ की स्थिति प्राप्त करेंगे, क्या होगा अगर दूसरे धागे ने some_data = new_dataआपके जागने के बाद सही किया था , लेकिन इससे पहले कि आपने कियाdata = some_data

आप वास्तव में इस मामले की रक्षा के लिए अपना स्वयं का म्यूटेक्स नहीं बना सकते हैं

while(1) {

    pthread_cond_wait(&cond); //imagine cond_wait did not have a mutex
    pthread_mutex_lock(&mutex);
    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

काम नहीं करेगा, म्यूटेक्स को जगाने और हथियाने के बीच दौड़ की स्थिति का एक मौका अभी भी है। Puteread_cond_wait से पहले म्यूटेक्स को रखने से आपको मदद नहीं मिलती है, क्योंकि अब आप प्रतीक्षा करते समय म्यूटेक्स को पकड़ लेंगे - अर्थात निर्माता कभी भी म्यूटेक्स को हड़पने में सक्षम नहीं होगा। (ध्यान दें, इस मामले में आप निर्माता को संकेत देने के लिए एक दूसरा शर्त चर बना सकते हैं जो आपके साथ किया गया है some_data- हालांकि यह जटिल हो जाएगा, खासकर यदि आप कई उत्पादकों / उपभोक्ताओं को चाहते हैं।)

इस प्रकार आपको स्थिति से प्रतीक्षा / जागने पर म्यूटेक्स को परमाणु रूप से छोड़ने / हथियाने का एक तरीका चाहिए। यही कारण है कि pthread condition वेरिएबल्स करता है, और यहाँ आप क्या करेंगे:

while(1) {
    pthread_mutex_lock(&mutex);
    while(some_data == NULL) { // predicate to acccount for spurious wakeups,would also 
                               // make it robust if there were several consumers
       pthread_cond_wait(&cond,&mutex); //atomically lock/unlock mutex
    }

    char *data = some_data;
    some_data = NULL;
    pthread_mutex_unlock(&mutex);
    handle(data);
}

(निर्माता को स्वाभाविक रूप से एक ही सावधानी बरतने की आवश्यकता होगी, हमेशा एक ही म्यूटेक्स के साथ 'some_data' की रखवाली करें, और यह सुनिश्चित करें कि यह some_data को अधिलेखित नहीं करता है यदि some_data वर्तमान में है! = NULL)


while (some_data != NULL)लूप-टू-लूप नहीं होना चाहिए ताकि यह कम से कम एक बार स्थिति चर का इंतजार करे?
जज मेगार्डन

3
नहीं। आप वास्तव में जिस चीज का इंतजार कर रहे हैं, वह 'some_data' के लिए गैर-अशक्त होना है। यदि यह "पहली बार" गैर-अशक्त है, तो आप म्यूटेक्स को पकड़ रहे हैं और सुरक्षित रूप से डेटा का उपयोग कर सकते हैं। यदि आपके पास लूप / जबकि लूप होता है तो आपको अधिसूचना याद आती है यदि कोई आपके द्वारा प्रतीक्षा करने से पहले स्थिति चर का संकेत देता है (यह win32 पर पाई गई घटनाओं की तरह कुछ भी नहीं है जो तब तक संकेतित रहते हैं जब तक कोई उनके लिए इंतजार नहीं करता है)
nos

4
मैं बस इस सवाल पर लड़खड़ाया और स्पष्ट रूप से, यह अजीब है कि यह जवाब है, जो सही है, paxdiablo के जवाब की तुलना में बहुत कम अंक हैं, जिसमें निश्चित दोष हैं (परमाणुता अभी भी आवश्यक है, म्यूटेक्स केवल स्थिति को संभालने के लिए आवश्यक है। संभालने या सूचित करने के लिए नहीं)। मुझे लगता है कि बस कैसे stackoverflow काम करता है ...
stefaanv

@stefaanv, यदि आप खामियों का विस्तार करना चाहते हैं, तो मेरे जवाब के लिए टिप्पणी के रूप में, इसलिए मैं उन्हें समय से पहले देखता हूं, बजाय महीनों बाद :-), मुझे उन्हें ठीक करने में खुशी होगी। आपके संक्षिप्त वाक्यांश वास्तव में मुझे यह बताने के लिए पर्याप्त विस्तार नहीं देते हैं कि आप क्या कहना चाह रहे हैं।
paxdiablo

1
@ नहीं, नहीं while(some_data != NULL)होना चाहिए while(some_data == NULL)?
एरिक जेड

30

POSIX कंडीशन वैरिएबल स्टेटलेस हैं। इसलिए राज्य को बनाए रखना आपकी जिम्मेदारी है। चूंकि राज्य दोनों थ्रेड्स द्वारा एक्सेस किया जाएगा जो प्रतीक्षा करता है और थ्रेड्स जो अन्य थ्रेड्स को प्रतीक्षा करना बंद करने के लिए कहते हैं, इसे एक mxx द्वारा संरक्षित किया जाना चाहिए। अगर आपको लगता है कि आप म्यूटेक्स के बिना कंडीशन वेरिएबल्स का उपयोग कर सकते हैं, तो आपने यह नहीं समझा है कि कंडीशन वेरिएबल स्टेटलेस हैं।

हालत चर एक शर्त के आसपास बनाए गए हैं। एक स्थिति चर पर प्रतीक्षा करने वाले थ्रेड्स कुछ स्थिति के लिए प्रतीक्षा कर रहे हैं। थ्रेड्स जो सिग्नल कंडीशन वैरिएबल उस स्थिति को बदलते हैं। उदाहरण के लिए, एक थ्रेड कुछ डेटा के आने की प्रतीक्षा कर रहा होगा। कुछ अन्य थ्रेड नोटिस कर सकते हैं कि डेटा आ गया है। "डेटा आ गया है" शर्त है।

यहाँ एक शर्त चर का क्लासिक उपयोग है, सरलीकृत:

while(1)
{
    pthread_mutex_lock(&work_mutex);

    while (work_queue_empty())       // wait for work
       pthread_cond_wait(&work_cv, &work_mutex);

    work = get_work_from_queue();    // get work

    pthread_mutex_unlock(&work_mutex);

    do_work(work);                   // do that work
}

देखें कि थ्रेड कैसे काम के लिए इंतजार कर रहा है। कार्य म्यूटेक्स द्वारा संरक्षित है। प्रतीक्षा म्यूटेक्स को जारी करती है ताकि एक और धागा इस धागे को कुछ काम दे सके। यहां बताया गया है कि यह कैसे संकेत दिया जाएगा:

void AssignWork(WorkItem work)
{
    pthread_mutex_lock(&work_mutex);

    add_work_to_queue(work);           // put work item on queue

    pthread_cond_signal(&work_cv);     // wake worker thread

    pthread_mutex_unlock(&work_mutex);
}

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


1
या, इसे और अधिक संक्षेप में रखने के लिए, स्थिति चर का पूरा बिंदु एक परमाणु "अनलॉक और प्रतीक्षा" ऑपरेशन प्रदान करना है। म्यूटेक्स के बिना, अनलॉक करने के लिए कुछ भी नहीं होगा।
डेविड श्वार्ट्ज

क्या आप स्टेटलेस का मतलब समझाएंगे ?
एसएनआर

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

16

सभी हालत चर कार्यों के लिए एक म्यूटेक्स की आवश्यकता होती है: केवल प्रतीक्षा संचालन करते हैं। संकेत और प्रसारण कार्यों को म्यूटेक्स की आवश्यकता नहीं होती है। एक शर्त चर भी एक विशिष्ट म्यूटेक्स के साथ स्थायी रूप से जुड़ा नहीं है; बाहरी म्यूटेक्स स्थिति चर की रक्षा नहीं करता है। यदि किसी स्थिति चर में आंतरिक स्थिति होती है, जैसे कि प्रतीक्षा थ्रेड की एक कतार, तो इसे शर्त चर के अंदर एक आंतरिक लॉक द्वारा संरक्षित किया जाना चाहिए।

प्रतीक्षा परिचालनाएँ एक स्थिति चर और म्यूटेक्स को एक साथ लाती हैं, क्योंकि:

  • एक थ्रेड ने म्यूटेक्स को लॉक कर दिया है, साझा चर पर कुछ अभिव्यक्ति का मूल्यांकन किया है और इसे गलत पाया है, जैसे कि इसे प्रतीक्षा करने की आवश्यकता है।
  • थ्रेड को अनिवार्य रूप से म्यूटेक्स के मालिक से स्थानांतरित करना चाहिए , स्थिति पर प्रतीक्षा करने के लिए।

इस कारण से, प्रतीक्षा ऑपरेशन म्यूटेक्स और स्थिति दोनों के तर्क के रूप में लेता है: ताकि यह म्यूटेक्स के मालिक होने से लेकर प्रतीक्षा तक एक थ्रेड के परमाणु हस्तांतरण का प्रबंधन कर सके, ताकि थ्रेड खोई हुई रेस की स्थिति में शिकार न हो ।

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

स्थिति परिवर्तनशील प्रतीक्षा फ़ंक्शंस खोए हुए जागने से बचने के लिए सुनिश्चित करती हैं कि कॉल थ्रेड को म्यूटेक्स को छोड़ने से पहले वेकअप को मज़बूती से पकड़ने के लिए पंजीकृत किया गया है। यह असंभव होगा अगर हालत चर प्रतीक्षा फ़ंक्शन ने म्यूटेक्स को तर्क के रूप में नहीं लिया।


क्या आप संदर्भ प्रदान कर सकते हैं कि प्रसारण कार्यों को म्यूटेक्स प्राप्त करने की आवश्यकता नहीं है? MSVC पर प्रसारण को नजरअंदाज कर दिया जाता है।
xvan

@xvan POSIX pthread_cond_broadcastऔर pthread_cond_signalऑपरेशंस (कि यह एसओ सवाल है के बारे में) म्यूटेक्स को एक तर्क के रूप में भी नहीं लेते हैं; केवल शर्त। POSIX कल्पना यहाँ है । म्यूटेक्स केवल प्रतीक्षा करने वाले धागे में क्या होता है के संदर्भ में उल्लेख किया गया है जब वे जागते हैं।
काज

क्या आप स्टेटलेस का मतलब समझाएंगे ?
एसएनआर

1
@snr एक स्टेटलेस सिंक्रोनाइज़ेशन ऑब्जेक्ट सिग्नलिंग से संबंधित किसी भी राज्य को याद नहीं रखता है। जब संकेत दिया जाता है, अगर कुछ इस पर इंतजार कर रहा है, तो यह जाग गया है, अन्यथा जागना भूल गया है। हालत चर इस तरह से स्टेटलेस हैं। सिंक्रनाइज़ेशन को विश्वसनीय बनाने के लिए आवश्यक स्थिति को सही ढंग से लिखे गए तर्क के अनुसार, एप्लिकेशन द्वारा बनाए रखा जाता है और म्यूटेक्स द्वारा संरक्षित किया जाता है, जो शर्त चर के साथ संयोजन में उपयोग किया जाता है।
कज़

7

मुझे इस पृष्ठ के समान संक्षिप्त और पठनीय होने के लिए अन्य उत्तर नहीं मिले । आम तौर पर वेटिंग कोड कुछ इस तरह दिखता है:

mutex.lock()
while(!check())
    condition.wait()
mutex.unlock()

wait()म्यूटेक्स में लपेटने के तीन कारण हैं :

  1. एक म्यूटेक्स के बिना एक और धागा signal()पहले हो सकता था wait()और हम इस जाग को याद करेंगे।
  2. आम तौर check()पर एक और धागे से संशोधन पर निर्भर है, तो आप वैसे भी इस पर आपसी बहिष्कार की जरूरत है।
  3. यह सुनिश्चित करने के लिए कि सर्वोच्च प्राथमिकता वाला धागा पहले निकलता है (म्यूटेक्स के लिए कतार अनुसूचक को यह तय करने की अनुमति देती है कि आगे कौन जाता है)।

तीसरा बिंदु हमेशा एक चिंता का विषय नहीं है - ऐतिहासिक संदर्भ लेख से इस बातचीत से जुड़ा हुआ है ।

इस तंत्र के संबंध में अक्सर अजीब-अप का उल्लेख किया जाता है (अर्थात प्रतीक्षा की signal()जा रही है, बिना बुलाया जा रहा है)। हालाँकि, इस तरह की घटनाओं को नियंत्रित किया जाता है check()


4

हालत चर एक म्यूटेक्स के साथ जुड़े हुए हैं क्योंकि यह एकमात्र तरीका है जिससे वह दौड़ से बच सकता है जिसे वह बचने के लिए डिज़ाइन किया गया है।

// incorrect usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    pthread_mutex_unlock(&mutex);
    if (ready) {
        doWork();
    } else {
        pthread_cond_wait(&cond1); // invalid syntax: this SHOULD have a mutex
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
pthread_mutex_unlock(&mutex);
pthread_cond_signal(&cond1);

Now, lets look at a particularly nasty interleaving of these operations

pthread_mutex_lock(&mutex);
bool ready = protectedReadyToRunVariable;
pthread_mutex_unlock(&mutex);
                                 pthread_mutex_lock(&mutex);
                                 protectedReadyToRuNVariable = true;
                                 pthread_mutex_unlock(&mutex);
                                 pthread_cond_signal(&cond1);
if (ready) {
pthread_cond_wait(&cond1); // uh o!

इस बिंदु पर, कोई थ्रेड नहीं है जो स्थिति चर का संकेत देने वाला है, इसलिए थ्रेड 1 हमेशा के लिए इंतजार करेगा, भले ही संरक्षितReadyToRunVariable का कहना है कि यह जाने के लिए तैयार है!

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

// correct usage:
// thread 1:
while (notDone) {
    pthread_mutex_lock(&mutex);
    bool ready = protectedReadyToRunVariable
    if (ready) {
        pthread_mutex_unlock(&mutex);
        doWork();
    } else {
        pthread_cond_wait(&mutex, &cond1);
    }
}

// signalling thread
// thread 2:
prepareToRunThread1();
pthread_mutex_lock(&mutex);
   protectedReadyToRuNVariable = true;
   pthread_cond_signal(&mutex, &cond1);
pthread_mutex_unlock(&mutex);

3

जब आप कॉल करते हैं तो म्यूटेक्स लॉक होना चाहिए pthread_cond_wait; जब आप इसे एटोमिक रूप से कहते हैं, तो दोनों म्यूटेक्स को अनलॉक कर देते हैं और फिर कंडीशन पर ब्लॉक हो जाते हैं। एक बार जब स्थिति का संकेत मिलता है तो यह इसे फिर से लॉक कर देता है और वापस लौट जाता है।

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


तो ... क्या मेरे लिए एक कारण के लिए है सिर्फ म्युटेक्स हमेशा-अनलॉक कर दिया है छोड़ देते हैं, और उसके बाद सही इंतजार कर से पहले यह ताला, और उसके बाद सही खत्म प्रतीक्षा करने के बाद इसे अनलॉक?
ELLIOTTCABLE

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

तो ... मुझे पहले कंडीशनवर के म्यूटेक्स पर वेट-ऑन-म्यूटेक्स करना चाहिए , कंडिशनर पर इंतजार करने से पहले? मुझे यकीन नहीं है कि मैं बिल्कुल समझ पा रहा हूं।
ELLIOTTCABLE

2
@elliottcable: म्यूटेक्स को पकड़े बिना, आप कैसे जान सकते हैं कि आपको इंतजार करना चाहिए या नहीं? क्या होगा अगर तुम बस के लिए इंतजार कर रहे हैं क्या हुआ?
डेविड श्वार्ट्ज

1

यदि आप स्थिति चर का एक वास्तविक उदाहरण चाहते हैं तो मैंने कक्षा में एक अभ्यास किया:

#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "unistd.h"

int compteur = 0;
pthread_cond_t varCond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex_compteur;

void attenteSeuil(arg)
{
    pthread_mutex_lock(&mutex_compteur);
        while(compteur < 10)
        {
            printf("Compteur : %d<10 so i am waiting...\n", compteur);
            pthread_cond_wait(&varCond, &mutex_compteur);
        }
        printf("I waited nicely and now the compteur = %d\n", compteur);
    pthread_mutex_unlock(&mutex_compteur);
    pthread_exit(NULL);
}

void incrementCompteur(arg)
{
    while(1)
    {
        pthread_mutex_lock(&mutex_compteur);

            if(compteur == 10)
            {
                printf("Compteur = 10\n");
                pthread_cond_signal(&varCond);
                pthread_mutex_unlock(&mutex_compteur);
                pthread_exit(NULL);
            }
            else
            {
                printf("Compteur ++\n");
                compteur++;
            }

        pthread_mutex_unlock(&mutex_compteur);
    }
}

int main(int argc, char const *argv[])
{
    int i;
    pthread_t threads[2];

    pthread_mutex_init(&mutex_compteur, NULL);

    pthread_create(&threads[0], NULL, incrementCompteur, NULL);
    pthread_create(&threads[1], NULL, attenteSeuil, NULL);

    pthread_exit(NULL);
}

1

यह वैचारिक आवश्यकता के बजाय एक विशिष्ट डिजाइन निर्णय प्रतीत होता है।

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

https://linux.die.net/man/3/pthread_cond_wait

म्यूटेक्स और कंडीशन वेरिएबल्स की विशेषताएं

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


0

उस बारे में कई टन हैं, फिर भी मैं निम्नलिखित उदाहरण के साथ इसे प्रकाशित करना चाहता हूं।

1 void thr_child() {
2    done = 1;
3    pthread_cond_signal(&c);
4 }

5 void thr_parent() {
6    if (done == 0)
7        pthread_cond_wait(&c);
8 }

कोड स्निपेट में क्या गलत है? आगे जाने से पहले बस कुछ हद तक विचार करें।


मुद्दा वास्तव में सूक्ष्म है। यदि माता-पिता आह्वान करते हैं thr_parent()और फिर मूल्य को बढ़ाते हैं done, तो यह देखेगा कि यह है 0और इस प्रकार सोने की कोशिश करें। लेकिन इससे पहले कि यह सोने के लिए प्रतीक्षा करने के लिए कहता है, माता-पिता 6-7 की लाइनों के बीच बाधित होते हैं, और बच्चा चलता है। बच्चा राज्य चर doneको 1और संकेतों में बदलता है, लेकिन कोई थ्रेड इंतजार नहीं कर रहा है और इस प्रकार कोई भी धागा जाग नहीं रहा है। जब माता-पिता फिर से दौड़ते हैं, तो यह हमेशा के लिए सो जाता है, जो वास्तव में अहंकारी है।

यदि व्यक्तिगत रूप से ताले प्राप्त किए जाते हैं तो क्या किया जाता है?

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