[C++] विंडोज थ्रेडिंग: _beginthread बनाम _beginthreadex बनाम CreateThread C ++


Answers

_beginthread() और _beginthreadex() बीच कई अंतर हैं। _beginthreadex() को CreateThread() (दोनों पैरामीटर में और यह कैसे व्यवहार करता है) की तरह कार्य करने के लिए बनाया गया था।

जैसा कि ड्रू हॉल का उल्लेख है, यदि आप सी / सी ++ रनटाइम का उपयोग कर रहे हैं, तो आपको CreateThread() बजाय _beginthread() / _beginthreadex() उपयोग करना होगा ताकि रनटाइम को अपना स्वयं का थ्रेड प्रारंभ करने का मौका मिले (थ्रेड स्थानीय स्टोरेज सेट अप करना , आदि।)।

अभ्यास में, इसका मतलब है कि CreateThread() को आपके कोड द्वारा सीधे उपयोग नहीं किया जाना चाहिए।

_beginthread() / _beginthreadex() लिए एमएसडीएन दस्तावेज़ों में _beginthread() पर काफी विस्तार है - एक और महत्वपूर्ण बात यह है कि _beginthread() द्वारा बनाए गए थ्रेड के लिए थ्रेड हैंडल सीआरटी द्वारा स्वचालित रूप से बंद हो जाता है जब थ्रेड निकलता है , "यदि _beginthread द्वारा उत्पन्न थ्रेड जल्दी से निकलता है, तो _beginthread के कॉलर पर वापस आने वाला हैंडल अमान्य हो सकता है, बदतर, किसी अन्य थ्रेड पर इंगित करें"।

यहां सीआरटी स्रोत में _beginthreadex() लिए टिप्पणियां क्या हैं:

Differences between _beginthread/_endthread and the "ex" versions:

1)  _beginthreadex takes the 3 extra parameters to CreateThread
  which are lacking in _beginthread():
    A) security descriptor for the new thread
    B) initial thread state (running/asleep)
    C) pointer to return ID of newly created thread

2)  The routine passed to _beginthread() must be __cdecl and has
  no return code, but the routine passed to _beginthreadex()
  must be __stdcall and returns a thread exit code.  _endthread
  likewise takes no parameter and calls ExitThread() with a
  parameter of zero, but _endthreadex() takes a parameter as
  thread exit code.

3)  _endthread implicitly closes the handle to the thread, but
  _endthreadex does not!

4)  _beginthread returns -1 for failure, _beginthreadex returns
  0 for failure (just like CreateThread).

जनवरी 2013 अपडेट करें :

वीएस 2012 के लिए सीआरटी में _beginthreadex() में प्रारंभिक प्रारंभिकता का एक अतिरिक्त बिट है: यदि प्रक्रिया एक "पैक किया गया ऐप" है (यदि कुछ उपयोगी GetCurrentPackageId() से लौटाया जाता है) रनटाइम नए बनाए गए धागे पर एमटीए शुरू करेगा।

Question

थ्रेड, _beginthread , _beginthreadx या CreateThread शुरू करने का बेहतर तरीका क्या है?

मैं यह निर्धारित करने की कोशिश कर रहा हूं कि _beginthread , _beginthreadex और CreateThread के फायदे / नुकसान क्या हैं। ये सभी फ़ंक्शंस एक नए बनाए गए धागे में थ्रेड हैंडल लौटाते हैं, मुझे पहले से ही पता है कि जब कोई त्रुटि होती है तो CreateThread थोड़ी अतिरिक्त जानकारी प्रदान करता है (इसे GetLastError कॉल करके चेक किया जा सकता है) ... लेकिन कुछ चीजें क्या हैं जिन पर मुझे विचार करना चाहिए मैं इन कार्यों का उपयोग कर रहा हूँ?

मैं एक विंडोज़ एप्लिकेशन के साथ काम कर रहा हूं, इसलिए क्रॉस-प्लेटफार्म संगतता पहले ही प्रश्न से बाहर है।

मैं msdn प्रलेखन के माध्यम से चला गया है और मैं समझ नहीं पा रहा हूं, उदाहरण के लिए, कोई भी CreateThread के बजाय _beginthread का उपयोग करने का निर्णय क्यों लेगा या इसके विपरीत।

चीयर्स!

अपडेट करें: ठीक है, सभी जानकारी के लिए धन्यवाद, मैंने कुछ स्थानों में भी पढ़ा है कि अगर मैं _beginthread() उपयोग _beginthread() हूं, तो मैं WaitForSingleObject() कॉल नहीं कर सकता, लेकिन अगर मैं थ्रेड में _endthread() को कॉल _endthread() हूं तो _endthread() नहीं करना चाहिए काम? वहां क्या सौदा है?




फ़ंक्शन हस्ताक्षर को देखते हुए, CreateThread लगभग _beginthreadex समान है।

_beginthread , _beginthreadx बनाम _beginthreadx

HANDLE WINAPI CreateThread(
  __in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  __in       SIZE_T dwStackSize,
  __in       LPTHREAD_START_ROUTINE lpStartAddress,
  __in_opt   LPVOID lpParameter,
  __in       DWORD dwCreationFlags,
  __out_opt  LPDWORD lpThreadId
);

uintptr_t _beginthread( 
   void( *start_address )( void * ),
   unsigned stack_size,
   void *arglist 
);

uintptr_t _beginthreadex( 
   void *security,
   unsigned stack_size,
   unsigned ( *start_address )( void * ),
   void *arglist,
   unsigned initflag,
   unsigned *thrdaddr 
);

यहां पर टिप्पणियां कहती हैं कि _beginthread या तो __cdecl या __clrcall कॉलिंग कन्वेंशन का उपयोग प्रारंभ बिंदु के रूप में कर सकता है, और _beginthreadex प्रारंभ बिंदु के लिए __stdcall या __clrcall उपयोग कर सकता है।

मुझे लगता है कि CreateThread में मेमोरी लीक पर किए गए किसी भी टिप्पणी को एक दशक पुराना है और शायद इसे अनदेखा किया जाना चाहिए।

दिलचस्प बात यह है कि, दोनों _beginthread* फ़ंक्शंस वास्तव में मेरी मशीन पर C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src में हुड के नीचे CreateThread को कॉल करते हैं।

// From ~line 180 of beginthreadex.c
/*
 * Create the new thread using the parameters supplied by the caller.
 */
if ( (thdl = (uintptr_t)
      CreateThread( (LPSECURITY_ATTRIBUTES)security,
                    stacksize,
                    _threadstartex,
                    (LPVOID)ptd,
                    createflag,
                    (LPDWORD)thrdaddr))
         == (uintptr_t)0 )
{
        err = GetLastError();
        goto error_return;
}



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

_beginthreadex() CreateThread() आसपास सी रैपर है जो सी विशिष्ट के लिए खाता है। यह मूल एकल थ्रेडेड सी एफ-एनएस को थ्रेड विशिष्ट स्टोरेज आवंटित करके बहुप्रचारित वातावरण में काम करता है।

यदि आप सीआरटी का उपयोग नहीं करते हैं तो आप CreateThread() को सीधे कॉल से नहीं बच सकते हैं। यदि आप सीआरटी का उपयोग करते हैं, तो आपको _beginthreadex() या कुछ सीआरटी स्ट्रिंग का उपयोग करना चाहिए f-ns पहले VC2005 से पहले ठीक से काम नहीं कर सकता है।




जब आप अपने कोड में किसी भी सीआरटी फ़ंक्शंस का उपयोग करते हैं microsoft.com/msj/0799/win32/win320799.aspx होता था। _beginthreadex() में CreateThread() समान पैरामीटर हैं और यह _beginthread() से अधिक बहुमुखी है। तो मैं आपको _beginthreadex() उपयोग करने की सलाह देता हूं।




यह _beginthreadex के मूल में _beginthreadex ( crt\src\threadex.c ):

    /*
     * Create the new thread using the parameters supplied by the caller.
     */
    if ( (thdl = (uintptr_t)
          CreateThread( (LPSECURITY_ATTRIBUTES)security,
                        stacksize,
                        _threadstartex,
                        (LPVOID)ptd,
                        createflag,
                        (LPDWORD)thrdaddr))
         == (uintptr_t)0 )
    {
            err = GetLastError();
            goto error_return;
    }

शेष _beginthreadex सीआरटी के लिए प्रति-थ्रेड डेटा संरचना आरंभ करता है।

_beginthread* का उपयोग करने का लाभ यह है कि थ्रेड से आपकी सीआरटी कॉल सही तरीके से काम करेगी।




_beginthread के साथ _beginthreadex तुलना में आप कर सकते हैं:

  1. सुरक्षा विशेषताओं को निर्दिष्ट करें।
  2. निलंबित राज्य में एक धागा शुरू करें।
  3. आप थ्रेड आईडी प्राप्त कर सकते हैं जिसका उपयोग OpenThread थ्रेड के साथ किया जा सकता है।
  4. कॉल सफल होने पर लौटा हुआ थ्रेड हैंडल मान्य होने की गारंटी है। वहां आपको CloseHandle साथ हैंडल को बंद करने की आवश्यकता है।
  5. लौटा हुआ थ्रेड हैंडल सिंक्रनाइज़ेशन एपीआई के साथ इस्तेमाल किया जा सकता है।

_beginthreadex निकटतम CreateThread जैसा दिखता है, लेकिन पूर्व एक सीआरटी कार्यान्वयन है और बाद वाला एक विंडोज एपीआई कॉल है। CreateThread के लिए दस्तावेज़ में निम्नलिखित अनुशंसाएं हैं:

निष्पादन योग्य में एक थ्रेड जो सी रन-टाइम लाइब्रेरी (सीआरटी) को _endthreadex करता है उसे CreateThread और ExitThread बजाय थ्रेड प्रबंधन के लिए _beginthreadex और _endthreadex फ़ंक्शंस का उपयोग करना चाहिए; इसके लिए सीआरटी के बहु-थ्रेडेड संस्करण के उपयोग की आवश्यकता है। यदि CreateThread का उपयोग करके बनाए गए थ्रेड सीआरटी को कॉल करते हैं, तो सीआरटी निम्न-स्मृति स्थितियों में प्रक्रिया को समाप्त कर सकता है।




अन्य उत्तर एक सी रन-टाइम फ़ंक्शन को कॉल करने के प्रभावों पर चर्चा करने में विफल रहते हैं जो Win32 API फ़ंक्शन को लपेटता है। डीएलएल लोडर लॉकिंग व्यवहार पर विचार करते समय यह महत्वपूर्ण है।

_beginthread{ex} कोई अन्य विशेष सी रनटाइम थ्रेड / फाइबर मेमोरी प्रबंधन करता है या नहीं, अन्य उत्तरों के बारे में चर्चा करता है, इसे एक डीएलएल में गतिशील लिंकिंग (इसे गतिशील लिंकिंग मानते हुए) में लागू किया जाता है, जो कि प्रक्रियाएं अभी तक लोड नहीं हो सकती हैं।

DllMain से _beginthread* को कॉल करना सुरक्षित नहीं है। मैंने विंडोज़ "AppInit_DLLs" सुविधा का उपयोग करके लोड किए गए डीएलएल को लिखकर इसका परीक्षण किया है। _beginthreadex (...) बजाय _beginthreadex (...) कॉल करने के बजाय विंडोज़ के बहुत से महत्वपूर्ण हिस्सों को बूटअप के दौरान काम करना बंद कर देता है क्योंकि कुछ प्रारंभिक कार्य करने के लिए लोडर लॉक को रिलीज़ करने के लिए DllMain एंट्री-पॉइंट डेडलॉक्स का इंतजार है ।

संयोग से, यह भी है कि kernel32.dll में बहुत अधिक ओवरलैपिंग स्ट्रिंग फ़ंक्शंस हैं जो सी रन-टाइम भी करता है - उसी प्रकार की स्थिति से बचने के लिए DllMain से उन लोगों का उपयोग करें।




दोनों के बीच अब कोई अंतर नहीं है।

मेमोरी लीक इत्यादि के बारे में सभी टिप्पणियां बहुत पुरानी <वीएस2005 संस्करणों पर आधारित हैं। मैंने साल पहले कुछ तनाव परीक्षण किया है और इस मिथक को खारिज कर सकता है। यहां तक ​​कि माइक्रोसॉफ्ट भी अपने उदाहरणों में शैलियों को मिश्रित करता है, लगभग कभी भी _beginthread का उपयोग नहीं करता है।