2009-05-13 14 views
75

हाल ही में, मैंने एक प्रश्न पूछा, शीर्षक के साथ "Is malloc thread safe?", और इसके अंदर मैंने पूछा, "क्या मॉलोक फिर से प्रवेशकर्ता है?"थ्रेडसेफ बनाम पुन: प्रवेश

मैं इस धारणा के तहत था कि सभी पुन: प्रवेशकर्ता थ्रेड-सुरक्षित हैं।

क्या यह धारणा गलत है?

उत्तर

38

पुन: प्रवेशी कार्यों सी

में (उदाहरण के लिए) है कि सी पुस्तकालय हेडर में संपर्क में हैं .. strtok() strtok_r बनाम ले वैश्विक चर पर भरोसा नहीं करते

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

errno, तथापि, POSIX सिस्टम पर एक अलग मामले (के रूप में है (और कैसे इस का कोई स्पष्टीकरण नहीं में oddball सभी कार्यों हो जाता है) :)

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

लेकिन, सभी कार्यों को किसी भी के लिए जांचने की आवश्यकता नहीं है। malloc() रैत्रांत होने के लिए, यह किसी भी थ्रेड के लिए प्रवेश बिंदु के दायरे से बाहर कुछ भी पर निर्भर नहीं करता कोई जरूरत नहीं है (और खुद को सुरक्षित थ्रेड है)।

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

यानी .:

static char *foo(unsigned int flags) 
{ 
    static char ret[2] = { 0 }; 

    if (flags & FOO_BAR) 
    ret[0] = 'c'; 
    else if (flags & BAR_FOO) 
    ret[0] = 'd'; 
    else 
    ret[0] = 'e'; 

    ret[1] = 'A'; 

    return ret; 
} 

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

पूरी तरह कार्यात्मक प्रोग्रामिंग में, रैत्रांत अक्सर धागा सुरक्षित संकेत नहीं करता है, यह, आदि समारोह प्रवेश बिंदु, प्रत्यावर्तन के लिए पारित परिभाषित या अनाम कार्यों के व्यवहार पर निर्भर करेगा

लिए एक बेहतर तरीका 'धागा सुरक्षित' डाल समवर्ती पहुँच है, जो बेहतर जरूरत दिखाता लिए सुरक्षित है।

+2

रैत्रांत धागा सुरक्षित संकेत नहीं करता है। शुद्ध कार्य थ्रेड-सुरक्षा का संकेत देते हैं। –

+0

ग्रेट उत्तर टिम। बस स्पष्ट करने के लिए, आपकी "अक्सर" से मेरी समझ यह है कि थ्रेड-सुरक्षित पुनर्वित्त का संकेत नहीं देता है, लेकिन पुनर्विक्रेता भी थ्रेड-सुरक्षित नहीं दर्शाता है। क्या आप एक पुनर्विक्रेता फ़ंक्शन का उदाहरण ढूंढ पाएंगे जो * थ्रेड-सुरक्षित नहीं है? – Riccardo

+0

@ टिम पोस्ट "संक्षेप में, पुनर्विक्रेता का अर्थ अक्सर थ्रेड सुरक्षित होता है (जैसा कि" यदि आप थ्रेड का उपयोग कर रहे हैं तो उस फ़ंक्शन के पुनर्वित्त संस्करण का उपयोग करें "), लेकिन थ्रेड सुरक्षित हमेशा पुनः प्रवेश करने का मतलब नहीं है।" qt [कहते हैं] (http://qt-project.org/doc/qt-4.8/threads-reentrancy.html) विपरीत: "इसलिए, एक थ्रेड-सुरक्षित फ़ंक्शन हमेशा पुनर्वित्तक होता है, लेकिन एक पुनर्विक्रेता फ़ंक्शन हमेशा थ्रेड नहीं होता है- सुरक्षित। " – 4pie0

53

यह परिभाषा पर निर्भर करता है।उदाहरण Qt uses के लिए निम्नलिखित:

  • एक धागा सुरक्षित * समारोह से अधिक थ्रेड से एक साथ कहा जा सकता है, तब भी जब आमंत्रण साझा डेटा का उपयोग करें, क्योंकि साझा किए गए डेटा के सभी संदर्भ क्रमांकित हैं।

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

इसलिए, एक धागा सुरक्षित समारोह हमेशा रैत्रांत है, लेकिन एक रैत्रांत समारोह हमेशा धागा सुरक्षित नहीं है।

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

लेकिन वे भी चेतावनी देते हैं:

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

+1

पुनर्वित्त की यह परिभाषा बहुत मजबूत है। – qweruiop

+4

डाउनवोट। एक थ्रेड-सुरक्षित फ़ंक्शन हमेशा पुनर्वित्त नहीं होता है। –

+0

यदि कोई वैश्विक/स्थैतिक var का उपयोग नहीं करता है तो एक फ़ंक्शन पुनर्वित्तक और थ्रेड-सुरक्षित दोनों होता है। थ्रेड - सुरक्षित: जब कई धागे एक ही समय में आपके फ़ंक्शन को चलाते हैं, तो क्या कोई दौड़ है ?? यदि आप वैश्विक var का उपयोग करते हैं, तो इसे सुरक्षित रखने के लिए लॉक का उपयोग करें। तो यह थ्रेड-सुरक्षित है। पुनर्वित्त: यदि आपके फ़ंक्शन निष्पादन के दौरान कोई सिग्नल होता है, और सिग्नल में फिर से अपना फ़ंक्शन कॉल करें, तो यह सुरक्षित है ??? ऐसे मामले में, कोई एकाधिक धागे नहीं हैं। यह सबसे अच्छा है कि आप किसी भी स्थैतिक/वैश्विक var का उपयोग इसे पुन: प्रस्तुत करने के लिए नहीं करते हैं, या उदाहरण के लिए 3. –

42

टीएल; डीआर: एक फ़ंक्शन दोबारा, थ्रेड-सुरक्षित, दोनों या न तो हो सकता है।

thread-safety और reentrancy के लिए विकिपीडिया लेख पढ़ने योग्य हैं। यहाँ कुछ उद्धरण नहीं हैं:

एक समारोह है धागा सुरक्षित यदि:

यह केवल एक तरह से गारंटी देता है कि एक ही समय में धागे कई द्वारा सुरक्षित निष्पादन में डेटा संरचनाओं साझा manipulates।

एक समारोह रैत्रांत है यदि:

यह किसी भी बिंदु पर इसके निष्पादन दौरान बाधित किया जा सकता है और फिर सुरक्षित रूप से फिर से ("फिर से प्रवेश किया") कहा जाता है अपने पिछले आमंत्रण पूरा क्रियान्वयन से पहले ।

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

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

उदाहरण

(थोड़ा विकिपीडिया लेख से संशोधित)

उदाहरण 1: थ्रेड-सुरक्षित नहीं, रैत्रांत नहीं

/* As this function uses a non-const global variable without 
    any precaution, it is neither reentrant nor thread-safe. */ 

int t; 

void swap(int *x, int *y) 
{ 
    t = *x; 
    *x = *y; 
    *y = t; 
} 

उदाहरण 2: धागा सुरक्षित , पुनर्वित्त नहीं

/* We use a thread local variable: the function is now 
    thread-safe but still not reentrant (within the 
    same thread). */ 

__thread int t; 

void swap(int *x, int *y) 
{ 
    t = *x; 
    *x = *y; 
    *y = t; 
} 

उदाहरण 3: थ्रेड-सुरक्षित नहीं, रैत्रांत

/* We save the global state in a local variable and we restore 
    it at the end of the function. The function is now reentrant 
    but it is not thread safe. */ 

int t; 

void swap(int *x, int *y) 
{ 
    int s; 
    s = t; 
    t = *x; 
    *x = *y; 
    *y = t; 
    t = s; 
} 

उदाहरण 4: धागा सुरक्षित, रैत्रांत

/* We use a local variable: the function is now 
    thread-safe and reentrant, we have ascended to 
    higher plane of existence. */ 

void swap(int *x, int *y) 
{ 
    int t; 
    t = *x; 
    *x = *y; 
    *y = t; 
} 
+3

मुझे पता है कि मुझे धन्यवाद देने के लिए टिप्पणी करने की आवश्यकता नहीं है, लेकिन यह सर्वोत्तम चित्रों में से एक है पुनः प्रवेशकर्ता और थ्रेड सुरक्षित कार्यों के बीच मतभेद। विशेष रूप से आपने बहुत संक्षिप्त स्पष्ट शब्दों का उपयोग किया है, और 4 श्रेणियों के बीच अंतर करने के लिए एक महान उदाहरण समारोह चुना है। तो धन्यवाद! – ryyker

+0

यदि कोई वैश्विक/स्थैतिक var का उपयोग नहीं करता है तो एक फ़ंक्शन पुनर्वित्तक और थ्रेड-सुरक्षित दोनों होता है। थ्रेड - सुरक्षित: जब कई धागे एक ही समय में आपके फ़ंक्शन को चलाते हैं, तो क्या कोई दौड़ है ?? यदि आप वैश्विक var का उपयोग करते हैं, तो इसे सुरक्षित रखने के लिए लॉक का उपयोग करें। तो यह थ्रेड-सुरक्षित है। पुनर्वित्त: यदि आपके फ़ंक्शन निष्पादन के दौरान कोई सिग्नल होता है, और सिग्नल में फिर से अपना फ़ंक्शन कॉल करें, तो यह सुरक्षित है ??? ऐसे मामले में, कोई एकाधिक धागे नहीं हैं।आप इसे किसी भी स्थिर/वैश्विक var का उपयोग पुनर्वित्त करने के लिए नहीं करेंगे ... –

+0

ऐसा लगता है कि थैला उदाहरण 3 पुनर्वित्त नहीं है: यदि सिग्नल हैंडलर, 't = * x' के बाद बाधा डालने पर, 'स्वैप()' को कॉल करता है, तब 'टी' ओवरराइड हो जाएगा, जिससे अप्रत्याशित परिणाम सामने आएंगे। – rom1v

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