2012-12-16 15 views
7

मैं बहु थ्रेड प्रोग्राम लिखता हूं।सुरक्षित रूप से थ्रेड बंद करें

मैं पूछना चाहता हूं कि TerminateThread और ExitThread के बीच क्या अंतर है?

यह मेरा कोड स्निपेट जब WM_DESTROY प्राप्त हुई है:

void CleanAll() 
{ 
    DWORD dwExit[MAX_THREAD]; 
    for(int i = 0; i < MAX_THREAD; i++) 
    { 
     GetExitCodeThread(hThread[i], &dwExit[i]); 
     // I used ExitThread(dwExit[i]); previously 
     TerminateThread(hThread[i], dwExit[i]); 
     CloseHandle(hThread[i]); 
    } 
} 

मैं ExitThread() पहले से इस्तेमाल किया, लेकिन कार्य प्रबंधक में मेरा कार्यक्रम stikk, तो मैं TerminateThread() के लिए इसे बदल और मेरे कार्यक्रम कार्य प्रबंधक से चला गया है।

कोई भी अग्रिम स्पष्टीकरण बहुत सराहना की है।

उत्तर

1

क्या आपका थ्रेड पूरा होने पर आपकी प्रक्रिया समाप्त होनी चाहिए? आपके द्वारा वर्णित समस्या के लिए कई स्पष्टीकरण हो सकते हैं। यदि आप पूरी प्रक्रिया को समाप्त करना चाहते हैं, तो बस ExitProcess पर कॉल करें।

TerminateThread के साथ समस्या यह है कि स्मृति स्मृति रिसाव होने की संभावना है। यह थ्रेड राज्य, न ही इसके आवंटित संसाधनों की परवाह नहीं करता है। यह सिंक्रनाइज़ेशन कैसे कर रहा है, इस पर निर्भर करता है कि यह एक डेडलॉक भी हो सकता है। दूसरे शब्दों में, यह इसे सुंदरता से समाप्त नहीं करता है।

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

+0

आपके उत्तर jweyrich के लिए धन्यवाद, मेरी समस्या हल हो गई है, मैं सेल्बी से निर्देश का पालन करता हूं, और जैसा कि आपने कहा था, यह सिर्फ थ्रेड के फ़ंक्शन से वापस आ गया है। – user1888256

10

टर्मिनेट थ्रेड बलों बाहर निकलने के लिए एक और थ्रेड। आपको इसे हर कीमत पर कॉल करने से बचना चाहिए क्योंकि यह बिना किसी सफाई के किसी भी मौके के अपने ट्रैक में थ्रेड मृत को रोक देगा। इसमें आवंटित कोई भी सीआरटी स्मृति शामिल है।

ExitThread के लिए वर्तमान में को अपने आप को अच्छी और साफ करने के लिए चल रहा है। जब आप इसे ऊपर बुलाते हैं, तो संभवतः आपने मुख्य (यूआई) थ्रेड को बाहर निकलने के लिए मजबूर कर दिया है और संभवतः चल रहे धागे को अभी भी चारों ओर घूमते हुए छोड़ दिया है। इसलिए, आपका प्रोग्राम अभी भी कार्य प्रबंधक में प्रमाणित के रूप में चल रहा था। GetExitCodeThread भी असफल होने की संभावना थी क्योंकि थ्रेड वास्तव में बाहर नहीं निकले थे।

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

bool global_Need_ToExit; // use a bool or replace with an event handle the thread shoudl wait on 

void CleanAll() 
{ 
    //signal all threads to exit 
    global_Need_ToExit = true; 

    DWORD dwExit[MAX_THREAD]; 
    for(int i = 0; i < MAX_THREAD; i++) 
    { 
     // actually wait for the thread to exit 
     WaitForSingleObject(hThread[i], WAIT_INFINITE); 

     // get the thread's exit code (I'm not sure why you need it) 
     GetExitCodeThread(hThread[i], &dwExit[i]); 

     // cleanup the thread 
     CloseHandle(hThread[i]); 
     hThread[i] = NULL; 
    } 
} 

DWORD __stdcall YourThreadFunction(void* pData) 
{ 

    while (global_Need_To_Exit == false) 
    { 
     // do more work 
    } 

    return 0; // same as ExitThread(0); 
} 
+0

इस उद्देश्य के साथ, किसी को गैर-परमाणु प्रकार का उपयोग इसे 'अस्थिर' के रूप में घोषित किए बिना नहीं करना चाहिए। कंपाइलर्स आम तौर पर लूप को अनुकूलित करते हैं, इसलिए अन्य धागे को एक अद्यतन मान नहीं दिखाई दे सकता है। एक परिवर्तनीय 'अस्थिर' घोषित करके आप संकलक को बताते हैं कि उसे प्रोसेसर रजिस्टर या कैश से पहले पढ़ने वाले मान को मानने के बजाय हर बार अपने भंडारण स्थान से अपना मान फिर से पढ़ना चाहिए। इसके अलावा, महान जवाब! +1 – jweyrich

+0

@ सेल्बी: धन्यवाद सेल्बी, आपकी सलाह मेरे लिए एक आकर्षण की तरह काम करती है – user1888256

+0

@jweyrich - प्रिंसिपल में सहमत। मान लीजिए कि वह कम से कम एक गैर-इनलाइन फ़ंक्शन कॉल करता है जो कि वेरिएबल के बीच है, वह ठीक होना चाहिए, क्योंकि संकलक यह नहीं मान सकता कि चर कॉल फ़ंक्शन कॉल के बीच नहीं बदलेगा। उन्हें तकनीकी रूप से मल्टीकोर/मल्टीप्रोक आर्किटेक्चर में बेहतर सुरक्षा के लिए "कंडिटोनल वेरिएबल" की आवश्यकता होती है, जिसे इंटरलॉक एक्सचेंज * फनक्स या उस चर पर किसी भी लॉक फ़ंक्शन का उपयोग करके आसानी से कार्यान्वित किया जाता है। – selbie

0

आप SingleWaitObjects या QueueUserAPC उपयोग कर सकते हैं, लेकिन आप यकीन है कि धागा इन async वस्तुओं की जाँच करें या यह अंत है में से एक के लिए प्रतीक्षा करें और सामान्य रूप से धागा समाप्त करने के लिए बंद हो जाता है कि बनाने के लिए है .. लिनक्स में आप संकेत

उपयोग कर सकते हैं
+0

आपके उत्तर सीएमएफ़-सीएमएसी के लिए धन्यवाद। मेरी समस्या अब चली गई है। मैं सिर्फ सेल्बी से निर्देश का पालन करता हूं। – user1888256

0

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

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

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

बेशक, आप केवल टर्मिनट थ्रेड के साथ एक थ्रेड को रोक सकते हैं जैसे कि आप प्रक्रिया को मारने के लिए टास्ककिल या CTRL-C का उपयोग कर सकते हैं। ExitThread एक क्लीन-शट डाउन सिग्नल बनाने के लिए एक विंडोज-विशिष्ट तरीका है जैसा कि मैंने (4) में वर्णन किया है, लेकिन आपको अभी भी काम करने के लिए थोड़ी देर-लूप & "WaitForSingleObject" (3) और विंडोज-विशिष्ट हैंडल (2) की आवश्यकता है।

2

क्षमा करें, लेकिन शीर्ष वोट दिया गया जवाब ExitThread का उपयोग करने के लिए कहता है क्योंकि यह "खुद को अच्छी और साफ तरीके से रोक देगा"। यह वास्तव में सच नहीं है। दस्तावेज़ीकरण में कहा गया है: निकास थ्रेड सी कोड में थ्रेड से बाहर निकलने का पसंदीदा तरीका है। हालांकि, सी ++ कोड में, किसी भी विनाशक को बुलाया जा सकता है या किसी भी अन्य स्वचालित सफाई के पहले धागा बाहर निकलता है। इसलिए, सी ++ कोड में, आपको अपने थ्रेड फ़ंक्शन से वापस जाना चाहिए। मैंने फोरम से एक्ज़िट थ्रेड का उपयोग करने के लिए सलाह का पालन करके यह कठिन तरीका सीखा और फिर घंटों खर्च कर रहा था कि मेरी सभी मेमोरी लीक कहां से आ रही थीं। हाँ, यह सी ++ के लिए है, लेकिन यही सवाल इस संबंध में है। और वास्तव में, यहां तक ​​कि सी के लिए भी एक चेतावनी है "स्थिर सी रन-टाइम लाइब्रेरी (सीआरटी) से जुड़ी निष्पादन योग्य में एक धागा को _beginthread और _Tththread को CreateThread और ExitThread के बजाय थ्रेड प्रबंधन के लिए उपयोग करना चाहिए। ऐसा करने में विफलता जब थ्रेड ExitThread को कॉल करता है तो छोटे मेमोरी लीक में परिणाम होता है। " परिणामों से अवगत किए बिना बाहर निकलें थ्रेड का उपयोग न करें!

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