वास्तव में एक रीक्रेंट फंक्शन क्या है?


198

अधिकांश की बार , reentrance की परिभाषा से उद्धृत किया गया है विकिपीडिया :

एक कंप्यूटर प्रोग्राम या रूटीन को रीवेंटेंट के रूप में वर्णित किया जाता है यदि इसे सुरक्षित रूप से फिर से बुलाया जा सकता है इससे पहले कि इसके पिछले आह्वान को पूरा किया गया हो (यानी इसे सुरक्षित रूप से समवर्ती रूप से निष्पादित किया जा सकता है)। एक कंप्यूटर प्रोग्राम या दिनचर्या के लिए:

  1. कोई स्थिर (या वैश्विक) गैर-स्थिर डेटा नहीं रखना चाहिए।
  2. स्थैतिक (या वैश्विक) गैर-स्थिर डेटा को पता वापस नहीं करना चाहिए।
  3. कॉल करने वाले द्वारा दिए गए डेटा पर ही काम करना चाहिए।
  4. सिंगलटन संसाधनों के लिए तालों पर निर्भर नहीं होना चाहिए।
  5. अपने स्वयं के कोड को संशोधित नहीं करना चाहिए (जब तक कि अपने स्वयं के अनूठे धागे के भंडारण में निष्पादित न हो)
  6. गैर-रीएंन्ट्रेंट कंप्यूटर प्रोग्राम या रूटीन को कॉल नहीं करना चाहिए।

कैसे सुरक्षित है रूप परिभाषित किया गया है?

यदि किसी कार्यक्रम को सुरक्षित रूप से समवर्ती रूप से निष्पादित किया जा सकता है , तो क्या इसका मतलब हमेशा यह होता है कि यह प्रतिशोधी है?

वास्तव में छह बिंदुओं के बीच सामान्य धागा क्या है जिसका मुझे ध्यान रखना चाहिए कि मैं रीएन्स्ट्रेंट क्षमताओं के लिए अपने कोड की जांच कर रहा हूं?

इसके अलावा,

  1. क्या सभी पुनरावर्ती कार्य हैं?
  2. क्या सभी थ्रेड-सेफ फंक्शन रीवेंटेंट हैं?
  3. क्या सभी पुनरावर्ती और थ्रेड-सुरक्षित फ़ंक्शन रीइंटरेंट हैं?

इस सवाल को लिखते समय, एक बात दिमाग में आती है: क्या रीट्रेन और थ्रेड सेफ्टी जैसी शर्तें बिल्कुल सही हैं? के लिए, यदि वे नहीं हैं, तो यह प्रश्न बहुत सार्थक नहीं है।


6
दरअसल, मैं पहली सूची में # 2 से असहमत हूं। आप फिर से प्रवेश समारोह से जो कुछ भी आप चाहते हैं, उसके लिए एक पता वापस कर सकते हैं - सीमा इस बात पर है कि आप कॉलिंग कोड में उस पते के साथ क्या करते हैं।

2
@Neil लेकिन रीवेंट्रेंट फ़ंक्शन के लेखक के अनुसार यह निश्चित रूप से नियंत्रित नहीं कर सकता है कि निश्चित रूप से कॉलर को निश्चित रूप से स्टेंट (या वैश्विक) गैर-स्थिर डेटा के लिए एक पता नहीं लौटना चाहिए, क्योंकि यह ट्रेंट रीटरेंट है?
Robben_Ford_Fan_boy

2
@drelihan यह किसी भी फ़ंक्शन के लेखक की ज़िम्मेदारी नहीं है (पुनरावृत्ति या नहीं) यह नियंत्रित करने के लिए कि एक कॉलर एक लौटे मूल्य के साथ क्या करता है। उन्हें निश्चित रूप से कहना चाहिए कि कॉलर इसके साथ क्या कर सकता है, लेकिन यदि कॉलर कुछ और करने का विकल्प चुनता है - तो फोन करने वाले के लिए कठिन भाग्य।

"थ्रेड-सेफ" अर्थहीन है जब तक कि आप यह भी निर्दिष्ट नहीं करते कि धागे क्या कर रहे हैं, और उनके कार्य का अपेक्षित प्रभाव क्या है। लेकिन शायद यह एक अलग सवाल होना चाहिए।

मैं मतलब के लिए सुरक्षित रूप से लेता हूं, व्यवहार शेड्यूलिंग की परवाह किए बिना अच्छी तरह से परिभाषित और नियतात्मक है।
अट्टुरसम

जवाबों:


191

1. कैसे सुरक्षित है रूप परिभाषित किया गया है?

शब्दार्थ। इस मामले में, यह एक कठिन परिभाषित शब्द नहीं है। इसका मतलब सिर्फ यह है कि "आप ऐसा कर सकते हैं, जोखिम के बिना"।

2. यदि किसी कार्यक्रम को सुरक्षित रूप से समवर्ती रूप से निष्पादित किया जा सकता है, तो क्या इसका अर्थ हमेशा यह होता है कि वह प्रतिशोधी है?

नहीं।

उदाहरण के लिए, हमारे पास C ++ फ़ंक्शन है जो एक लॉक और पैरामीटर के रूप में कॉलबैक दोनों लेता है:

#include <mutex>

typedef void (*callback)();
std::mutex m;

void foo(callback f)
{
    m.lock();
    // use the resource protected by the mutex

    if (f) {
        f();
    }

    // use the resource protected by the mutex
    m.unlock();
}

एक अन्य फ़ंक्शन को समान म्यूटेक्स को लॉक करने की आवश्यकता हो सकती है:

void bar()
{
    foo(nullptr);
}

पहली नजर में, सब कुछ ठीक लगता है ... लेकिन रुकिए:

int main()
{
    foo(bar);
    return 0;
}

यदि म्यूटेक्स पर ताला पुनरावर्ती नहीं है, तो यहां मुख्य धागा में क्या होगा:

  1. mainबुला लेंगे foo
  2. foo ताला का अधिग्रहण करेगा।
  3. fooबुलाएगा bar, जो बुलाएगाfoo
  4. दूसरा foo लॉक प्राप्त करने का प्रयास करेगा, विफल हो जाएगा और इसके जारी होने की प्रतीक्षा करेगा।
  5. गतिरोध।
  6. ओह ...

ठीक है, मैंने कॉलबैक चीज़ का उपयोग करके धोखा दिया। लेकिन समान प्रभाव वाले कोड के अधिक जटिल टुकड़ों की कल्पना करना आसान है।

3. छः बिंदुओं के बीच सामान्य धागा क्या है, जिसका मुझे ध्यान रखना चाहिए कि मैं रीएन्ट्रेंट क्षमताओं के लिए अपने कोड की जांच कर रहा हूं?

आप एक समस्या को सूँघ सकते हैं यदि आपके फ़ंक्शन में एक परिवर्तनीय स्थायी संसाधन तक पहुँच है / है, या एक ऐसे फ़ंक्शन तक पहुँच देता है, जिसमें बदबू आती है

( ठीक है, हमारे कोड के 99% को गंध चाहिए, फिर ... इसे संभालने के लिए अंतिम अनुभाग देखें ... )

इसलिए, अपने कोड का अध्ययन करते हुए, उन बिंदुओं में से एक को आपको सचेत करना चाहिए:

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

ध्यान दें कि गैर-पुनर्संयोजन वायरल है: एक फ़ंक्शन जो एक संभावित गैर-रीवेंट्रेंट फ़ंक्शन को कॉल कर सकता है, उसे रीएन्ट्रेंट नहीं माना जा सकता है।

ध्यान दें, भी, कि C ++ विधियाँ सूंघती हैं क्योंकि उनकी पहुँच होती है this, इसलिए आपको यह सुनिश्चित करने के लिए कोड का अध्ययन करना चाहिए कि उनके पास कोई मज़ेदार बातचीत नहीं है।

4.1। क्या सभी पुनरावर्ती कार्य हैं?

नहीं।

मल्टीथ्रेडेड मामलों में, एक साझा संसाधन तक पहुंचने वाले एक पुनरावर्ती कार्य को एक ही समय में कई थ्रेड्स द्वारा बुलाया जा सकता है, जिसके परिणामस्वरूप खराब / दूषित डेटा हो सकता है।

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

4.2। क्या सभी थ्रेड-सेफ फंक्शन रीवेंटेंट हैं?

ऊपर दिए गए उदाहरण में, मैंने दिखाया कि कैसे एक जाहिरा तौर पर थ्रेड्सफ़ेफ़ फ़ंक्शन रेंटेंट नहीं था। ठीक है, मैंने कॉलबैक पैरामीटर के कारण धोखा दिया। लेकिन फिर, एक गैर-पुनरावर्ती लॉक को दो बार हासिल करने से एक धागे को गतिरोध करने के कई तरीके हैं।

4.3। क्या सभी पुनरावर्ती और थ्रेड-सुरक्षित फ़ंक्शन रीइंटरेंट हैं?

मैं "हाँ" कहूँगा यदि "पुनरावर्ती" से आपका मतलब है "पुनरावर्ती-सुरक्षित"।

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

समस्या इस गारंटी का मूल्यांकन कर रही है ... ^ _ ^

5. क्या श्रद्धा और धागा सुरक्षा जैसी शर्तें बिल्कुल सही हैं, यानी क्या उनकी कोई ठोस परिभाषा है?

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

6. एक उदाहरण

मान लें कि आपके पास एक ऑब्जेक्ट है, जिसमें एक विधि है जिसे संसाधन का उपयोग करने की आवश्यकता है:

struct MyStruct
{
    P * p;

    void foo()
    {
        if (this->p == nullptr)
        {
            this->p = new P();
        }

        // lots of code, some using this->p

        if (this->p != nullptr)
        {
            delete this->p;
            this->p = nullptr;
        }
    }
};

पहली समस्या यह है कि अगर किसी तरह इस फ़ंक्शन को पुनरावर्ती कहा जाता है (यानी यह फ़ंक्शन खुद को प्रत्यक्ष या अप्रत्यक्ष रूप से कहता है), तो कोड संभवतः दुर्घटनाग्रस्त हो जाएगा, क्योंकि this->p जाएगा अंतिम कॉल के अंत में हटा दिया जाएगा, और अभी भी संभवतः समाप्ति से पहले उपयोग किया जाएगा पहली कॉल की।

इस प्रकार, यह कोड पुनरावर्ती-सुरक्षित नहीं है

हम इसे ठीक करने के लिए एक संदर्भ काउंटर का उपयोग कर सकते हैं:

struct MyStruct
{
    size_t c;
    P * p;

    void foo()
    {
        if (c == 0)
        {
            this->p = new P();
        }

        ++c;
        // lots of code, some using this->p
        --c;

        if (c == 0)
        {
            delete this->p;
            this->p = nullptr;
        }
    }
};

इस तरह, कोड पुनरावर्ती-सुरक्षित हो जाता है ... लेकिन यह अभी भी है क्योंकि मुद्दों multithreading के रैत्रांत नहीं है: हम होना चाहिए यकीन है कि के संशोधन cऔर की patomically किया जाएगा, एक का उपयोग कर पुनरावर्ती म्युटेक्स (सभी नहीं mutexes पुनरावर्ती हैं):

#include <mutex>

struct MyStruct
{
    std::recursive_mutex m;
    size_t c;
    P * p;

    void foo()
    {
        m.lock();

        if (c == 0)
        {
            this->p = new P();
        }

        ++c;
        m.unlock();
        // lots of code, some using this->p
        m.lock();
        --c;

        if (c == 0)
        {
            delete this->p;
            this->p = nullptr;
        }

        m.unlock();
    }
};

और निश्चित रूप से, यह सभी मानता है कि lots of codeइसका उपयोग करने सहित, स्वयं ही प्रतिरूप हैp

और ऊपर दिया गया कोड दूरस्थ रूप से अपवाद-सुरक्षित भी नहीं है , लेकिन यह एक और कहानी है ... ^ _ ^

7. अरे 99% हमारा कोड रीएन्स्ट्रेंट नहीं है!

स्पेगेटी कोड के लिए यह काफी सही है। लेकिन अगर आप अपने कोड को सही ढंग से विभाजित करते हैं, तो आप रीटर्रेंस की समस्याओं से बच जाएंगे।

7.1। सुनिश्चित करें कि सभी कार्यों में कोई स्थिति नहीं है

उन्हें केवल मापदंडों, अपने स्वयं के स्थानीय चर, राज्य के बिना अन्य कार्यों का उपयोग करना चाहिए, और यदि वे बिल्कुल भी वापस आते हैं तो डेटा की प्रतियां लौटाएं।

7.2। सुनिश्चित करें कि आपकी वस्तु "पुनरावर्ती-सुरक्षित" है

एक ऑब्जेक्ट विधि तक पहुंच है this, इसलिए यह ऑब्जेक्ट के एक ही उदाहरण के सभी तरीकों के साथ एक राज्य साझा करता है।

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

7.3। सुनिश्चित करें कि आपकी सभी वस्तुएँ सही ढंग से संलग्न हैं

किसी और को अपने आंतरिक डेटा तक पहुंच नहीं होनी चाहिए:

    // bad
    int & MyObject::getCounter()
    {
        return this->counter;
    }

    // good
    int MyObject::getCounter()
    {
        return this->counter;
    }

    // good, too
    void MyObject::getCounter(int & p_counter)
    {
        p_counter = this->counter;
    }

यदि उपयोगकर्ता द्वारा डेटा के पते को पुनः प्राप्त किया जाता है, तो भी एक कॉस्ट संदर्भ लौटाना खतरनाक हो सकता है, क्योंकि कोड के कुछ अन्य हिस्से को कॉर्ड संदर्भ को बताए बिना कोड को संशोधित कर सकता है।

7.4। सुनिश्चित करें कि उपयोगकर्ता जानता है कि आपकी वस्तु थ्रेड-सुरक्षित नहीं है

इस प्रकार, उपयोगकर्ता थ्रेड के बीच साझा की गई वस्तु का उपयोग करने के लिए म्यूटेक्स का उपयोग करने के लिए जिम्मेदार है।

STL से ऑब्जेक्ट को थ्रेड-सुरक्षित (प्रदर्शन समस्याओं के कारण) डिज़ाइन किया गया है, और इस प्रकार, यदि कोई उपयोगकर्ता साझा करना चाहता है std::string दो थ्रेड्स के बीच , तो उपयोगकर्ता को कॉन्सिक्वेंसी प्राइमेटिक्स के साथ इसकी पहुंच की सुरक्षा करनी चाहिए;

7.5। सुनिश्चित करें कि आपका थ्रेड-सुरक्षित कोड पुनरावर्ती-सुरक्षित है

इसका मतलब है कि यदि आप एक ही संसाधन को एक ही धागे से दो बार उपयोग कर सकते हैं, तो पुनरावर्ती म्यूटेक्स का उपयोग करें।


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

क्या आप पहले उदाहरण में म्यूटेक्स में गुजरने से चूक गए?
detly

@paercebal: आपका उदाहरण गलत है। आपको वास्तव में कॉलबैक से परेशान होने की आवश्यकता नहीं है, एक साधारण पुनरावृत्ति में एक ही समस्या होगी, हालांकि एक ही समस्या है कि आप यह कहना बिल्कुल भूल गए कि ताला कहाँ आवंटित किया गया है।
यत्रिल्ल

3
@ वायट्रिल: मुझे लगता है कि आप पहले उदाहरण के बारे में बात कर रहे हैं। मैंने "कॉलबैक" का उपयोग किया, क्योंकि संक्षेप में, एक कॉलबैक से गंध आती है। बेशक, एक पुनरावर्ती फ़ंक्शन में एक ही समस्या होगी, लेकिन आमतौर पर, कोई भी आसानी से एक फ़ंक्शन और इसकी पुनरावर्ती प्रकृति का विश्लेषण कर सकता है, इस प्रकार, यह पता लगाता है कि क्या यह पुनरावृत्ति है या पुनरावृत्ति के लिए ठीक है। कॉलबैक, दूसरे हाथों में, का अर्थ है कि कॉलबैक कॉल करने वाले फ़ंक्शन के लेखक को कोई जानकारी नहीं है कि कॉलबैक क्या कर रहा है, इसलिए इस लेखक को यह सुनिश्चित करने में मुश्किल हो सकती है कि उसका फ़ंक्शन रीवेंटेंट है। यह वह कठिनाई है जिसे मैं दिखाना चाहता था।
15

1
@ गैब 是 是: मैंने पहला उदाहरण सही किया। धन्यवाद! एक सिग्नल हैंडलर अपनी समस्याओं के साथ आएगा, पुनर्संयोजन से अलग, आमतौर पर, जब एक सिग्नल उठाया जाता है, तो आप वास्तव में विशेष रूप से घोषित वैश्विक चर को बदलने से परे कुछ भी नहीं कर सकते।
पियरसेबल

21

"सुरक्षित रूप से" को सामान्य अर्थ के रूप में परिभाषित किया गया है - इसका अर्थ है "अन्य चीजों के साथ हस्तक्षेप किए बिना अपनी बात को सही ढंग से करना"। आप जिन छह बिंदुओं का हवाला देते हैं, वे स्पष्ट रूप से प्राप्त करने की आवश्यकताओं को व्यक्त करते हैं।

आपके 3 प्रश्नों के उत्तर 3 × "नहीं" हैं।


क्या सभी पुनरावर्ती कार्य हैं?

नहीं!

उदाहरण के लिए, एक ही वैश्विक / स्थिर डेटा तक पहुँचने पर, एक पुनरावर्ती कार्य के दो एक साथ आक्रमण आसानी से एक दूसरे को खराब कर सकते हैं।


क्या सभी थ्रेड-सेफ फंक्शन रीवेंटेंट हैं?

नहीं!

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


क्या सभी पुनरावर्ती और थ्रेड-सुरक्षित फ़ंक्शन रीइंटरेंट हैं?

नहीं!

ऊपर देखो।


10

आम धागा:

क्या व्यव्हार को अच्छी तरह से परिभाषित किया जाता है यदि दिनचर्या को बाधित होने के दौरान कहा जाता है?

यदि आपके पास इस तरह का कोई कार्य है:

int add( int a , int b ) {
  return a + b;
}

तब यह किसी बाहरी अवस्था पर निर्भर नहीं है। व्यवहार अच्छी तरह से परिभाषित है।

यदि आपके पास इस तरह का कोई कार्य है:

int add_to_global( int a ) {
  return gValue += a;
}

परिणाम कई थ्रेड्स पर अच्छी तरह से परिभाषित नहीं है। यदि समय गलत था तो सूचना खो सकती है।

एक रेंटेंट फ़ंक्शन का सबसे सरल रूप कुछ ऐसा है जो विशेष रूप से पारित तर्कों और निरंतर मूल्यों पर संचालित होता है। कोई भी चीज विशेष रूप से संभालती है या, अक्सर, रीएन्ट्रेंट नहीं होती है। और निश्चित रूप से तर्कों को उत्परिवर्ती ग्लोबल्स का संदर्भ नहीं देना चाहिए।


7

अब मुझे अपनी पिछली टिप्पणी पर विस्तार से बताना है। @paercebal का उत्तर गलत है। उदाहरण के कोड में किसी ने ध्यान नहीं दिया कि म्यूटेक्स जो पैरामीटर माना जाता है वास्तव में पारित नहीं हुआ था?

मैं निष्कर्ष पर विवाद करता हूं, मैं दावा करता हूं: एक समारोह के लिए निर्णायक की उपस्थिति में सुरक्षित होने के लिए इसे फिर से दर्ज करना होगा। इसलिए समवर्ती-सुरक्षित (आमतौर पर लिखित थ्रेड-सेफ़) का अर्थ है पुन: प्रवेश।

तर्कों के बारे में न तो थ्रेड सेफ और न ही री-एंट्रेंट के पास कुछ भी है: हम फ़ंक्शन के समवर्ती निष्पादन के बारे में बात कर रहे हैं, जो अभी भी असुरक्षित हो सकता है यदि अनुचित मापदंडों का उपयोग किया जाता है।

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

यह समझना महत्वपूर्ण है कि सामान्य तौर पर यह बकवास है से थ्रेड-सुरक्षित संचालन के लिए है जिसमें पैरामीटर शामिल हैं। यदि आपने कोई डेटाबेस प्रोग्रामिंग की है तो आप समझ जाएंगे। "परमाणु" क्या है की अवधारणा और एक म्यूटेक्स या किसी अन्य तकनीक द्वारा संरक्षित किया जा सकता है आवश्यक रूप से एक उपयोगकर्ता अवधारणा है: एक डेटाबेस पर लेनदेन को संसाधित करने के लिए कई गैर-बाधित संशोधनों की आवश्यकता हो सकती है। कौन कह सकता है कि किन लोगों को सिंक में रखा जाना चाहिए लेकिन क्लाइंट प्रोग्रामर?

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

वहाँ कई प्रोग्रामिंग सिस्टम हैं: Ocaml एक है, और मुझे लगता है कि पायथन के रूप में अच्छी तरह से, जिनके पास बहुत सारे गैर-रीएंन्ट्रेंट कोड हैं, लेकिन जो थ्रेड एसेस को इंटरलेव करने के लिए एक वैश्विक लॉक का उपयोग करता है। ये सिस्टम फिर से प्रवेश नहीं कर रहे हैं और वे थ्रेड-सुरक्षित या समवर्ती-सुरक्षित नहीं हैं, वे सुरक्षित रूप से केवल इसलिए काम करते हैं क्योंकि वे विश्व स्तर पर संगामिति को रोकते हैं।

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

malloc(heap*, size_t);

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

सामान्यतया, ताले चीजों को धागा-सुरक्षित नहीं बनाते हैं .. वे वास्तव में ग्राहक द्वारा स्वामित्व वाले संसाधन का प्रबंधन करने के लिए अनुचित तरीके से सुरक्षा को नष्ट करते हैं। ऑब्जेक्ट निर्माता द्वारा लॉकिंग किया जाना है, एकमात्र कोड है जो जानता है कि कितनी वस्तुएं बनाई गई हैं और उनका उपयोग कैसे किया जाएगा।


"इसलिए समवर्ती-सुरक्षित (आमतौर पर लिखित थ्रेड-सेफ़) का तात्पर्य पुनः प्रवेश होता है।" यह विकिपीडिया के उदाहरण "थ्रेड-सेफ लेकिन रीफ्रंट नहीं" के विपरीत है ।
मैगीयरो

3

सूचीबद्ध बिंदुओं के बीच "सामान्य धागा" (वाक्य का इरादा !?) है कि फ़ंक्शन को कुछ भी ऐसा नहीं करना चाहिए जो किसी भी फ़ंक्शन के किसी भी पुनरावर्ती या समवर्ती कॉल के व्यवहार को प्रभावित करे।

इसलिए उदाहरण के लिए स्थैतिक डेटा एक मुद्दा है क्योंकि यह सभी थ्रेड्स के स्वामित्व में है; यदि कोई कॉल एक स्थिर चर को संशोधित करता है तो सभी थ्रेड संशोधित डेटा का उपयोग करते हैं और इस प्रकार उनके व्यवहार को प्रभावित करते हैं। स्व संशोधित कोड (हालांकि शायद ही कभी सामना करना पड़ा, और कुछ मामलों में रोका गया) एक समस्या होगी, क्योंकि हालांकि कई थ्रेड होते हैं, कोड की केवल एक प्रति होती है; कोड आवश्यक स्थैतिक डेटा भी है।

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

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

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


1

आपके "इसके" सवालों के जवाब "नहीं", "नहीं" और "नहीं" हैं। सिर्फ इसलिए कि एक फ़ंक्शन पुनरावर्ती और / या थ्रेड सुरक्षित है, जो इसे फिर से प्रवेश नहीं करता है।

इनमें से प्रत्येक प्रकार का फ़ंक्शन आपके द्वारा उद्धृत सभी बिंदुओं पर विफल हो सकता है। (हालांकि मैं बिंदु 5 का 100% निश्चित नहीं हूं)।


1

"थ्रेड-सेफ" और "री-एंट्रेंट" शब्द का अर्थ केवल और ठीक वैसा ही है जैसा उनकी परिभाषाएँ कहती हैं। इस संदर्भ में "सुरक्षित" का मतलब केवल यह है कि आप नीचे दी गई परिभाषा को क्या कहते हैं।

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

रिकर्सिव फंक्शन कुछ भी हो सकता है और री-एंट्रेंट में थ्रेड-सेफ की तुलना में एक मजबूत परिभाषा है इसलिए आपके गिने हुए प्रश्नों के उत्तर सभी नहीं हैं।

री-एंट्रेंट की परिभाषा को पढ़कर, कोई इसे एक फ़ंक्शन के रूप में संक्षेपित कर सकता है जो कि किसी भी चीज को संशोधित नहीं करेगा जिसे आप इसे संशोधित करने के लिए कहते हैं। लेकिन आपको केवल सारांश पर भरोसा नहीं करना चाहिए।

बहु-थ्रेडेड प्रोग्रामिंग सामान्य मामले में सिर्फ अत्यंत कठिन है। यह जानना कि किसी के कोड का कौन सा हिस्सा फिर से प्रवेश करना इस चुनौती का एक हिस्सा है। थ्रेड सुरक्षा additive नहीं है। फिर से प्रवेश करने वाले कार्यों को एक साथ करने की कोशिश करने के बजाय, यह एक समग्र थ्रेड-सुरक्षित का उपयोग करना बेहतर है डिज़ाइन पैटर्न का उपयोग और इस पैटर्न का उपयोग करके अपने प्रोग्राम में प्रत्येक थ्रेड और साझा संसाधनों का उपयोग करें।

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