जब मुख्य () बाहर निकलता है तो एक अलग थ्रेड का क्या होता है?


153

मुझे लगता है कि मैं शुरू कर रहा हूँ std::threadऔर फिर detach()यह, इसलिए धागा निष्पादन जारी है, भले ही std::threadएक बार यह प्रतिनिधित्व किया है, गुंजाइश से बाहर चला जाता है।

आगे मान लें कि प्रोग्राम में अलग किए गए थ्रेड 1 में शामिल होने के लिए एक विश्वसनीय प्रोटोकॉल नहीं है , इसलिए main()बाहर निकलने पर थ्रेडेड थ्रेड अभी भी चलता है।

मुझे मानक (N3797 C ++ 14 ड्राफ्ट में अधिक सटीक) में कुछ भी नहीं मिल रहा है, जो बताता है कि क्या होना चाहिए, न तो 1.10 और न ही 30.3 में प्रासंगिक शब्द शामिल हैं।

1 एक और, शायद समतुल्य, सवाल यह है: "एक अलग किए गए धागे को फिर से शामिल किया जा सकता है", क्योंकि जो भी प्रोटोकॉल आप में शामिल होने के लिए आविष्कार कर रहे हैं, थ्रेडिंग अभी भी चल रही थी, और ओएस अनुसूचक हो सकता है सिगनलिंग के बाद एक घंटे के लिए सोने के लिए धागे को लगाने का फैसला करें ताकि प्राप्त अंत के लिए कोई रास्ता नहीं मिल सके।

यदि main()थ्रेडेड थ्रेड के साथ रनिंग अपरिभाषित व्यवहार है, तो कोई भी उपयोग std::thread::detach()अपरिभाषित व्यवहार है जब तक कि मुख्य धागा 2 से बाहर नहीं निकलता ।

इस प्रकार, main()अलग-थलग चलने वाले थ्रेड के साथ चलने से परिभाषित प्रभाव पड़ने चाहिए । सवाल यह है: जहां ( C ++ मानक में , POSIX नहीं, ओएस डॉक्स नहीं, ...) उन प्रभावों को परिभाषित किया गया है।

2 एक अलग किया हुआ धागा शामिल नहीं किया जा सकता (अर्थ में std::thread::join())। आप अलग किए गए थ्रेड्स से परिणाम की प्रतीक्षा कर सकते हैं (उदाहरण के लिए भविष्य के माध्यम से std::packaged_task, या एक गिनती सेमाफोर या एक ध्वज और एक स्थिति चर के द्वारा), लेकिन यह गारंटी नहीं देता है कि थ्रेड ने निष्पादन समाप्त कर दिया है । दरअसल, जब तक आप धागे की पहली स्वचालित वस्तु का नाशक में संकेतन हिस्सा रखा, वहाँ होगा , सामान्य रूप में, कोड (विनाशकर्ता) कि रन हो के बाद संकेत कोड। अगर ओएस परिणाम को भस्म करने के लिए मुख्य थ्रेड को शेड्यूल करता है और इससे पहले कि अलग किए गए थ्रेड खत्म होने से पहले बाहर निकलने के लिए विनाशकारी कहा जाता है, तो ^ क्या होने के लिए परिभाषित किया जाएगा?


5
मैं केवल [basic.start.term] / 4 में एक बहुत अस्पष्ट गैर-अनिवार्य नोट पा सकता हूं: " इन आवश्यकताओं को पूरा करने के लिए कॉल करने से पहले std::exitया बाहर निकलने के लिए हर धागे को समाप्त mainकरना पर्याप्त है, लेकिन आवश्यक नहीं है।" (पूरा पैराग्राफ प्रासंगिक हो सकता है) यह भी देखें [support.start.term] / 8 ( रिटर्न मिलने पर std::exitबुलाया जाता mainहै)
dyp

जवाबों:


45

मूल प्रश्न का उत्तर "जब main()बाहर निकलता है तो एक अलग थ्रेड का क्या होता है":

यह चलता रहता है (क्योंकि मानक यह नहीं कहता है कि यह रोक दिया गया है), और यह अच्छी तरह से परिभाषित है, जब तक कि यह न तो (थ्रेड_लोकल) अन्य थ्रेड्स के चर और न ही स्थिर वस्तुओं को छूता है।

यह प्रतीत होता है कि थ्रेड मैनेजरों को स्टैटिक ऑब्जेक्ट्स के रूप में अनुमति दी जा सकती है (नोट [basic.start.term] / 4 में उतना ही लिखा है, जितना पॉइंटर के लिए @dyp का धन्यवाद)।

समस्याएँ तब उत्पन्न होती हैं जब स्थैतिक वस्तुओं का विनाश समाप्त हो जाता है, क्योंकि तब निष्पादन एक ऐसे नियम में प्रवेश करता है जहां केवल सिग्नल हैंडलर में अनुमति दी गई कोड निष्पादित हो सकता है ( [basic.start.term] / 1, 1 वाक्य )। C ++ मानक लाइब्रेरी में से, वह केवल <atomic>लाइब्रेरी है ( [support.runtime] / 9, 2nd वाक्य )। विशेष रूप से, वह सामान्य तौर पर- इसे शामिल करता है condition_variable (यह कार्यान्वयन-परिभाषित है कि क्या सिग्नल हैंडलर में उपयोग करने के लिए बचत है, क्योंकि यह भाग नहीं है <atomic>)।

जब तक आप इस बिंदु पर अपना ढेर नहीं हटाते हैं, तब तक यह देखना मुश्किल है कि अपरिभाषित व्यवहार से कैसे बचा जाए।

दूसरे प्रश्न का उत्तर "थ्रेड्स को अलग किया जा सकता है, उसे फिर से जोड़ा जा सकता है":

हाँ, के साथ *_at_thread_exitकार्यों का परिवार ( notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit(), ...)।

के रूप में प्रश्न के फुटनोट [2] में बताया गया है, एक शर्त चर या एक सेमाफोर या एक परमाणु काउंटर संकेत (यह सुनिश्चित करना कि इसके निष्पादन के अंत के अर्थ में एक अलग धागे में शामिल होने के लिए पर्याप्त नहीं है है-क्या हुआ-से पहले की प्राप्ति वेटिंग थ्रेड द्वारा संकेतन कहा जाता है), क्योंकि, सामान्य तौर पर, notify_all()एक कंडीशन वैरिएबल, विशेष रूप से स्वचालित और थ्रेड-लोकल ऑब्जेक्ट के डिस्ट्रक्टर्स जैसे उदाहरण के बाद अधिक कोड निष्पादित होगा ।

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

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


17
क्या तुम इसके बारे में निश्चित हो? हर जगह मैंने परीक्षण किया (जीसीसी 5, क्लैंग 3.5, एमएसवीसी 14), सभी अलग किए गए धागे मारे जाते हैं जब मुख्य धागा बाहर निकलता है।
19

3
मेरा मानना ​​है कि मुद्दा यह नहीं है कि एक विशिष्ट कार्यान्वयन क्या करता है, लेकिन यह कैसे बचा जाए कि मानक अपरिभाषित व्यवहार के रूप में क्या परिभाषित करता है।
जॉन स्पेंसर

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

3
यदि आप नॉन-आईएसओ C ++ API का उपयोग कर रहे हैं तो यदि आप लौटने या mainकॉल pthread_exitकरने के बजाय कॉल करते हैं exitतो यह प्रक्रिया समाप्त होने के लिए अलग थ्रेड के लिए प्रतीक्षा करने का कारण होगा, और फिर exitअंतिम एक के बाद कॉल करेगा।
जोनाथन वेकली

3
"यह जारी है (क्योंकि मानक यह नहीं कहता है कि रोक दिया गया है)" -> क्या कोई मुझे बता सकता है कि एक धागा अपने कंटेनर प्रक्रिया को कैसे अंजाम दे सकता है?
गुप्त

42

थ्रेडिंग का पता लगाना

के अनुसार std::thread::detach:

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

से pthread_detach:

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

थ्रेडिंग थ्रेडिंग मुख्य रूप से संसाधनों को बचाने के लिए है, यदि आवेदन को समाप्त होने के लिए किसी थ्रेड के लिए प्रतीक्षा करने की आवश्यकता नहीं है (उदाहरण के लिए डेमॉन, जिसे प्रक्रिया समाप्ति तक चलना चाहिए):

  1. एप्लिकेशन साइड हैंडल को मुक्त करने के लिए: एक std::threadवस्तु को बिना शामिल किए गुंजाइश से बाहर जाने दे सकते हैं, जो आम तौर std::terminate()पर विनाश को बुलावा देता है ।
  2. थ्रेड से बाहर निकलते ही थ्रेड विशिष्ट संसाधनों ( टीसीबी ) को स्वचालित रूप से साफ करने की अनुमति देने के लिए , क्योंकि हम स्पष्ट रूप से निर्दिष्ट करते हैं, कि हम बाद में थ्रेड में शामिल होने में रुचि नहीं रखते हैं, इस प्रकार, एक पहले से अलग किए गए धागे में शामिल नहीं हो सकता है।

हत्या के धागे

प्रक्रिया समाप्ति पर व्यवहार मुख्य धागे के लिए एक ही है, जो कम से कम कुछ संकेतों को पकड़ सकता है। अन्य थ्रेड्स सिग्नल को संभाल सकते हैं या नहीं, यह महत्वपूर्ण नहीं है, क्योंकि मुख्य थ्रेड के सिग्नल हैंडलर इनवोकेशन के भीतर अन्य थ्रेड्स शामिल हो सकते हैं या समाप्त हो सकते हैं। (संबंधित जानकारी )

जैसा कि पहले ही कहा गया है, किसी भी धागे, चाहे अलग हो या न हो, अधिकांश ओएस पर इसकी प्रक्रिया के साथ मर जाएगा । इस प्रक्रिया को स्वयं एक संकेत को बढ़ाकर, कॉल exit()करके या मुख्य फ़ंक्शन से वापस करके समाप्त किया जा सकता है । हालाँकि, C ++ 11 अंतर्निहित OS के सटीक व्यवहार को परिभाषित करने की कोशिश नहीं कर सकता है, जबकि जावा वीएम के डेवलपर्स निश्चित रूप से इस तरह के अंतर को कुछ हद तक अमूर्त कर सकते हैं। AFAIK, विदेशी प्रक्रिया और थ्रेडिंग मॉडल आमतौर पर प्राचीन प्लेटफार्मों पर पाए जाते हैं (जिसमें C ++ 11 को शायद पोर्ट नहीं किया जाएगा) और विभिन्न एम्बेडेड सिस्टम, जो एक विशेष और / या सीमित भाषा पुस्तकालय कार्यान्वयन और सीमित भाषा समर्थन भी कर सकते हैं।

थ्रेड सपोर्ट

यदि थ्रेड समर्थित नहीं std::thread::get_id()हैं, तो एक अमान्य std::thread::idप्रक्रिया (डिफ़ॉल्ट निर्मित ) वापस करनी चाहिए क्योंकि एक सादा प्रक्रिया है, जिसे चलाने के लिए किसी थ्रेड ऑब्जेक्ट की आवश्यकता नहीं होती है और कंस्ट्रक्टर को एक std::threadफेंकना चाहिए std::system_error। यह है कि मैं आज के OSes के साथ संयोजन में C ++ 11 को कैसे समझता हूं। अगर थ्रेडिंग सपोर्ट वाला ओएस है, जो अपनी प्रक्रियाओं में एक मुख्य धागा नहीं रखता है, तो मुझे बताएं।

थ्रेड्स को नियंत्रित करना

यदि किसी को उचित शटडाउन के लिए एक धागे पर नियंत्रण रखने की आवश्यकता है, तो कोई ऐसा कर सकता है जो सिंक प्राइमिटिव और / या किसी प्रकार के झंडे का उपयोग करके कर सकता है। हालाँकि, इस मामले में, एक शटडाउन झंडे को शामिल करना, मेरे द्वारा पसंद किए जाने का एक तरीका है, क्योंकि थ्रेड को अलग करने से जटिलता बढ़ने का कोई मतलब नहीं है, क्योंकि संसाधनों को वैसे भी उसी समय मुक्त किया जाएगा, जहां std::threadऑब्जेक्ट के कुछ बाइट्स बनाम उच्च जटिलता और संभवतः अधिक सिंक आदिम स्वीकार्य होना चाहिए।


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

8
मैं वास्तव में यह नहीं देखता कि यह सवाल का जवाब कैसे देता है
माइकएम

18

निम्नलिखित कोड पर विचार करें:

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

void thread_fn() {
  std::this_thread::sleep_for (std::chrono::seconds(1)); 
  std::cout << "Inside thread function\n";   
}

int main()
{
    std::thread t1(thread_fn);
    t1.detach();

    return 0; 
}

इसे लिनक्स सिस्टम पर चलाने से, थ्रेड_fn से संदेश कभी भी प्रिंट नहीं होता है। बाहर निकलते thread_fn()ही OS वास्तव में साफ़ हो जाता main()है। जगह t1.detach()के साथ t1.join()हमेशा संदेश अपेक्षा के अनुरूप प्रिंट करता है।


यह व्यवहार बिल्कुल विंडोज पर होता है। इसलिए, ऐसा लगता है कि प्रोग्राम समाप्त होने पर विंडोज अलग थ्रेड्स को मारता है।
गुप्ता

17

कार्यक्रम से बाहर निकलने के बाद धागे का भाग्य अपरिभाषित व्यवहार है। लेकिन एक आधुनिक ऑपरेटिंग सिस्टम इसे बंद करने पर प्रक्रिया द्वारा बनाए गए सभी थ्रेड्स को साफ करेगा।

जब एक को अलग करते हुए std::thread, ये तीन स्थितियाँ बनी रहेंगी:

  1. *this अब किसी भी धागे का मालिक नहीं है
  2. joinable() हमेशा के बराबर होगा false
  3. get_id() बराबर होगा std::thread::id()

1
अपरिभाषित क्यों? क्योंकि मानक कुछ भी परिभाषित नहीं करता है? मेरे फुटनोट से, क्या ऐसा नहीं होगा कि detach()अपरिभाषित व्यवहार के लिए कोई कॉल किया जाए ? विश्वास करना मुश्किल ...
मार्क मुतज़ - mmutz

2
@ MarcMutz-mmutz यह इस अर्थ में अपरिभाषित है कि यदि प्रक्रिया समाप्त हो जाती है, तो धागे का भाग्य अपरिभाषित होता है।
सीजर

2
@ कैसर और मैं धागा खत्म होने से पहले कैसे सुनिश्चित नहीं करता हूं?
मिशल

6

जब मुख्य थ्रेड (यानी, मुख्य () फ़ंक्शन) को चलाने वाला थ्रेड समाप्त हो जाता है, तो प्रक्रिया समाप्त हो जाती है और अन्य सभी थ्रेड बंद हो जाते हैं।

संदर्भ: https://stackoverflow.com/a/4667273/2194843


0

अन्य थ्रेड्स को निष्पादन जारी रखने की अनुमति देने के लिए, मुख्य धागे को बाहर निकलने (3) के बजाय pthread_exit () कहकर समाप्त करना चाहिए। मुख्य में pthread_exit का उपयोग करना ठीक है। जब pthread_exit का उपयोग किया जाता है, तो मुख्य थ्रेड निष्पादित करना बंद कर देगा और अन्य सभी थ्रेड्स से बाहर निकलने तक ज़ोंबी (डिफेक्ट) स्थिति में रहेगा। यदि आप मुख्य धागे में pthread_exit का उपयोग कर रहे हैं, तो अन्य थ्रेड की स्थिति प्राप्त नहीं कर सकते हैं और अन्य थ्रेड्स के लिए क्लीन-अप नहीं कर सकते हैं (pthread_join (3) का उपयोग करके किया जा सकता है)। इसके अलावा, थ्रेड्स को अलग करना बेहतर है (pthread_detach (3)) ताकि थ्रेड संसाधनों को थ्रेड समाप्ति पर स्वचालित रूप से रिलीज़ किया जा सके। सभी थ्रेड्स से बाहर निकलने तक साझा संसाधन जारी नहीं किए जाएंगे।


@kgvinod, "pthread_exit (0);" "ti.detach ()" के बाद;
यशी

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