2012-06-06 19 views
20

का उपयोग करते समय मुख्य() बाहर निकलने के बाद बुलाया जाता है यदि निम्नलिखित उदाहरण उबंटू 12.04 पर क्लैंग 3.2 या जीसीसी 4.7 का उपयोग करके संकलित किया गया है तो निम्नलिखित उदाहरण सफलतापूर्वक चलाया जाता है (यानी लटका नहीं है), लेकिन हैंग अगर मैं वीएस 11 बीटा या वीएस2012 आरसी का उपयोग कर संकलित करता हूं।std :: thread :: join() vs2012 आरसी

#include <iostream> 
#include <string> 
#include <thread> 
#include "boost/thread/thread.hpp" 

void SleepFor(int ms) { 
    std::this_thread::sleep_for(std::chrono::milliseconds(ms)); 
} 

template<typename T> 
class ThreadTest { 
public: 
    ThreadTest() : thread_([] { SleepFor(10); }) {} 
    ~ThreadTest() { 
    std::cout << "About to join\t" << id() << '\n'; 
    thread_.join(); 
    std::cout << "Joined\t\t" << id() << '\n'; 
    } 
private: 
    std::string id() const { return typeid(decltype(thread_)).name(); } 
    T thread_; 
}; 

int main() { 
    static ThreadTest<std::thread> std_test; 
    static ThreadTest<boost::thread> boost_test; 
// SleepFor(100); 
} 

मुद्दा है कि std::thread::join() कभी नहीं देता है अगर यह बाद main से बाहर निकल गया है शुरू हो जाती है प्रकट होता है। इसे WaitForSingleObject पर _Thrd_join में cthread.c में परिभाषित किया गया है।

के अंत में SleepFor(100); को प्रोग्राम को ठीक से बाहर निकलने की अनुमति देता है, क्योंकि std_test गैर स्थैतिक बनाता है। boost::thread का उपयोग भी इस मुद्दे से बचाता है।

तो मैं जानना चाहता हूं कि क्या मैं यहां अपरिभाषित व्यवहार का आह्वान कर रहा हूं (मुझे असंभव लगता है), या अगर मुझे वीएस2012 के खिलाफ एक बग दर्ज करना चाहिए?

+0

बीटा और रिलीज उम्मीदवार मुद्दे? – Jaywalker

+0

@ जयवाकर मुझे लगता है कि मैं यही पूछ रहा हूं। क्या 'मुख्य' बाहर निकलने के बाद इसे शामिल होने पर 'शामिल() 'लटका नहीं होना उचित है। यदि ऐसा है, तो यह एक बग है जिसे मैं जल्द ही बाद में एमएस कनेक्ट में प्राप्त करना चाहता हूं। – Fraser

+0

क्या आप परीक्षण करने का प्रयास कर सकते हैं कि 'std :: thread :: joinable() 'अभी भी लटकने से पहले सच हो जाता है? – Plexico

उत्तर

22

VS2012 आरटीएम के साथ अपने कनेक्ट बग (https://connect.microsoft.com/VisualStudio/feedback/details/747145) में फ्रेजर का नमूना कोड के माध्यम से ट्रेसिंग deadlocking के एक काफी सीधा मामला दिखाने के लिए लगता है। यह संभवतः std::thread के लिए विशिष्ट नहीं है - संभावित _beginthreadex समान भाग्य का सामना करता है।

मैं डीबगर में क्या देखते निम्नलिखित है:

मुख्य थ्रेड पर, main() समारोह पूरा कर लिया है, इस प्रक्रिया को सफाई कोड, _EXIT_LOCK1 नामक एक महत्वपूर्ण अनुभाग हासिल कर ली है ThreadTest का नाशक कहा जाता है, और इंतज़ार कर रहा है (अनिश्चित काल तक) बाहर निकलने के लिए दूसरे धागे पर (join() पर कॉल के माध्यम से)।

दूसरा धागा का अनाम कार्य पूरा हो गया और _EXIT_LOCK1 महत्वपूर्ण अनुभाग प्राप्त करने के लिए थ्रेड क्लीनअप कोड में है। दुर्भाग्यवश, चीजों के समय के कारण (जिससे दूसरे थ्रेड के अज्ञात फ़ंक्शन का जीवनकाल main() फ़ंक्शन से अधिक हो जाता है) मुख्य धागा पहले से ही उस महत्वपूर्ण खंड का मालिक है।

डेडलॉक।

कोई भी चीज जो main() के जीवनकाल ऐसी है कि दूसरा धागा _EXIT_LOCK1 प्राप्त कर सकते हैं इससे पहले कि मुख्य थ्रेड गतिरोध स्थिति से बचा जाता है फैली हुई है। यही कारण है कि main() में नींद को अपूर्ण करने के परिणामस्वरूप एक साफ शटडाउन होता है।

वैकल्पिक रूप से, अगर आप ThreadTest स्थानीय चर से स्थिर कीवर्ड निकालने, नाशक कॉल (प्रक्रिया सफाई कोड के बजाय में) main() समारोह के अंत तक ले जाया जाता है जो फिर ब्लॉक जब तक दूसरा धागा से बाहर निकल गया है - से परहेज डेडलॉक स्थिति।

या आप ThreadTest पर एक फ़ंक्शन जोड़ सकते हैं जो join() पर कॉल करता है और main() के अंत में उस फ़ंक्शन को कॉल करता है - फिर से डेडलॉक स्थिति से परहेज करता है।

+0

अच्छा विश्लेषण। ~ –

+0

@CWoods - क्या आप अपने एमएस कनेक्ट बग रिपोर्ट के लिंक को अपने उत्तर में चिपका सकते हैं? मैं आपको सही के रूप में चिह्नित करना चाहता हूं और मेरा हटाना चाहता हूं। – Fraser

+0

@ फ्रैज़र - हो गया! मदद करने में खुशी हुई! – CWoods

1

मेरा मानना ​​है कि आपके धागे पहले ही समाप्त हो चुके हैं और आपके संसाधनों को आपके मुख्य कार्य को समाप्त करने और स्थिर विनाश से पहले मुक्त हो गए हैं। यह वीसी रनटाइम्स का व्यवहार कम से कम वीसी 6 पर वापस डेटिंग है।

Do child threads exit when the parent thread terminates

boost thread and process cleanup on windows

+0

यह उत्तर विंडोज़ के लिए विशिष्ट है, लेकिन मुझे लगता है कि आप वीएस का उपयोग कर रहे हैं। – TractorPulledPork

+0

मैं विंडोज का उपयोग कर रहा हूं, लेकिन मुझे यकीन नहीं है कि आपका जवाब दुर्भाग्य से सही है। मुख्य धागा वह है जो 'जॉइन' निष्पादित कर रहा है, भले ही फ़ंक्शन 'मुख्य' निकल गया हो, मुख्य थ्रेड समाप्त नहीं हुआ है। फिर भी धन्यवाद। – Fraser

+0

अधिक प्रासंगिक सामग्री: http://stackoverflow.com/questions/8001989/boost-thread-and-process-cleanup-on-windows – TractorPulledPork

-1

मैं एक दिन के लिए इस बग से जूझ कर दिया है, और पाया निम्नलिखित काम के आसपास है, जो निकला हो कम से कम गंदा चाल:

लौटने के बजाय, एक मानक Windows API समारोह कॉल का उपयोग कर सकते ExitThread() धागे को समाप्त करने के लिए। पाठ्यक्रम की यह विधि std :: थ्रेड ऑब्जेक्ट और संबंधित लाइब्रेरी की आंतरिक स्थिति को गड़बड़ कर सकती है, लेकिन चूंकि प्रोग्राम किसी भी तरह से समाप्त होने जा रहा है, ठीक है, तो हो।

#include <windows.h> 

template<typename T> 
class ThreadTest { 
public: 
    ThreadTest() : thread_([] { SleepFor(10); ExitThread(NULL); }) {} 
    ~ThreadTest() { 
    std::cout << "About to join\t" << id() << '\n'; 
    thread_.join(); 
    std::cout << "Joined\t\t" << id() << '\n'; 
    } 
private: 
    std::string id() const { return typeid(decltype(thread_)).name(); } 
    T thread_; 
}; 

शामिल() कॉल स्पष्ट रूप से सही तरीके से काम करता है। हालांकि, मैंने अपने समाधान में एक और अधिक सुरक्षित विधि का उपयोग करना चुना। कोई std :: thread :: native_handle() के माध्यम से थ्रेड हैंडल प्राप्त कर सकता है। ,

WaitForSingleObject(thread_.native_handle(), INFINITE); 
CloseHandle(thread_.native_handle()); 

इसके बाद, std :: धागा वस्तु नष्ट नहीं किया जाना चाहिए के रूप में नाशक धागा दूसरी बार में शामिल होने की कोशिश करेंगे: इस संभाल के साथ हम धागा शामिल होने के लिए सीधे Windows API कॉल कर सकते हैं। इसलिए हम प्रोग्राम बाहर निकलने पर std :: थ्रेड ऑब्जेक्ट को लटकते हैं।

6

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

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

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

तो, समस्या को ठीक करने के लिए, स्रोत मॉड्यूल के नीचे निम्न कोड डालें, उदाहरण के लिए कहीं नीचे मुख्य()।

#pragma warning(disable:4073) // initializers put in library initialization area 
#pragma init_seg(lib) 

#if _MSC_VER < 1900 
struct VS2013_threading_fix 
{ 
    VS2013_threading_fix() 
    { 
     _Cnd_do_broadcast_at_thread_exit(); 
    } 
} threading_fix; 
#endif 
+1

यह वर्कअराउंड विजुअल स्टूडियो 2013 अल्टीमेट में मेरे लिए काम नहीं करता है। – leetNightshade

+0

डीएलएल, एमएसवीसी ++ 2013 अपडेट 5 में सिंगलटन सक्रिय ऑब्जेक्ट के लिए मेरे लिए काम नहीं करता है। –

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