फ़ंक्शन ऑब्जेक्ट का उपयोग करके C ++ थ्रेड, कई विध्वंसक कैसे कहे जाते हैं लेकिन निर्माणकर्ता नहीं?


15

कृपया नीचे कोड स्निपेट ढूंढें:

class tFunc{
    int x;
    public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }
    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX(){ return x; }
};

int main()
{
    tFunc t;
    thread t1(t);
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

मुझे जो आउटपुट मिल रहा है वह है:

Constructed : 0x7ffe27d1b0a4
Destroyed : 0x7ffe27d1b06c
Thread is joining...
Thread running at : 11
Destroyed : 0x2029c28
x : 1
Destroyed : 0x7ffe27d1b0a4

मुझे भ्रम है कि 0x7ffe27d1b06c और 0x2029c28 पते वाले विध्वंसक कैसे और कोई निर्माणकर्ता नहीं कहलाते? जबकि क्रमशः पहला और अंतिम निर्माणकर्ता और विध्वंसक मेरे द्वारा बनाई गई वस्तु का है।


11
परिभाषित करें और अपने कॉपी-कॉटर और मूव-सेंट को भी लिख लें।
व्हाट्सक्राइग

अच्छी तरह से समझ लिया। चूंकि मैं ऑब्जेक्ट को कॉपी कंस्ट्रक्टर कहा जा रहा हूं, क्या मैं सही हूं? लेकिन, मूव कंस्ट्रक्टर कब कहा जाता है?
SHAHBAZ

जवाबों:


18

आप कॉपी-कंस्ट्रक्शन और मूव कंस्ट्रक्शन को इंस्ट्रूमेंट करने में चूक रहे हैं। आपके कार्यक्रम के लिए एक सरल संशोधन सबूत प्रदान करेगा कि जहां निर्माण हो रहे हैं।

कापी कंस्ट्रक्टर

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    tFunc t;
    thread t1{t};
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

आउटपुट (पते भिन्न होते हैं)

Constructed : 0x104055020
Copy constructed : 0x104055160 (source=0x104055020)
Copy constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104055020

कंस्ट्रक्टर और मूव कंस्ट्रक्टर को कॉपी करें

यदि आप एक चाल ctor प्रदान करते हैं, तो इसे कम-से-कम उन प्रतियों में से एक के लिए पसंद किया जाएगा:

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    tFunc(tFunc&& obj) : x(obj.x)
    {
        cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
        obj.x = 0;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    tFunc t;
    thread t1{t};
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

आउटपुट (पते भिन्न होते हैं)

Constructed : 0x104057020
Copy constructed : 0x104057160 (source=0x104057020)
Move constructed : 0x602000008a38 (source=0x104057160)
Destroyed : 0x104057160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104057020

संदर्भ लिपटा हुआ

यदि आप उन प्रतियों से बचना चाहते हैं तो आप अपने कॉल करने योग्य को रेफर रैपर ( std::ref) में लपेट सकते हैं । चूंकि आप tथ्रेडिंग भाग किए जाने के बाद उपयोग करना चाहते हैं , यह आपकी स्थिति के लिए व्यवहार्य है। व्यवहार में, आपको कॉल ऑब्जेक्ट्स के संदर्भ में थ्रेडिंग करते समय बहुत सावधान रहना चाहिए , क्योंकि ऑब्जेक्ट का जीवनकाल कम से कम तब तक बढ़ना चाहिए जब तक कि थ्रेड संदर्भ का उपयोग कर रहा हो।

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    tFunc(tFunc&& obj) : x(obj.x)
    {
        cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
        obj.x = 0;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    tFunc t;
    thread t1{std::ref(t)}; // LOOK HERE
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

आउटपुट (पते भिन्न होते हैं)

Constructed : 0x104057020
Thread is joining...
Thread running at : 11
x : 11
Destroyed : 0x104057020

नोट भले ही मैंने कॉपी-कॉटर और मूव-सीटर ओवरलोड्स को रखा हो, न तो कॉल किया गया था, क्योंकि रेफर रेपर अब कॉपी / स्थानांतरित होने वाली चीज है; यह संदर्भ नहीं है। इसके अलावा, यह अंतिम दृष्टिकोण बचाता है जो आप शायद देख रहे थे; t.xवापस main, वास्तव में, के लिए संशोधित है 11। यह पूर्व के प्रयासों में नहीं था। हालांकि, यह पर्याप्त तनाव नहीं कर सकता: ऐसा करने से सावधान रहें । वस्तु जीवनकाल महत्वपूर्ण है


हटो, और कुछ भी नहीं

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

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    tFunc(tFunc&& obj) : x(obj.x)
    {
        cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
        obj.x = 0;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    thread t1{tFunc()}; // LOOK HERE
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    return 0;
}

आउटपुट (पते भिन्न होते हैं)

Constructed : 0x104055040
Move constructed : 0x104055160 (source=0x104055040)
Move constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Destroyed : 0x104055040
Thread is joining...
Thread running at : 11
Destroyed : 0x602000008a38

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


5

टिप्पणियों में पोस्ट किए गए आपके अतिरिक्त प्रश्न के लिए:

मूव कंस्ट्रक्टर को कब बुलाया जाता है?

के निर्माता std::threadपहले अपने पहले तर्क (द्वारा decay_copy) की एक प्रति बनाता है - वह वह जगह है जहाँ कॉपी निर्माता कहा जाता है। (कृपया ध्यान दें कि के मामले में rvalue जैसे तर्क, thread t1{std::move(t)};या thread t1{tFunc{}};, चाल निर्माता बजाय कहा जा सकता है।)

परिणाम decay_copyएक अस्थायी है जो स्टैक पर रहता है। हालाँकि, चूंकि कॉलिंग थ्रेडdecay_copy द्वारा किया जाता है , यह अस्थायी इसके ढेर पर रहता है और std::thread::threadनिर्माणकर्ता के अंत में नष्ट हो जाता है । नतीजतन, अस्थायी रूप से सीधे नए बनाए गए थ्रेड द्वारा उपयोग नहीं किया जा सकता है।

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


ध्यान दें कि हम आश्चर्यचकित हो सकते हैं कि क्यों अस्थायी अस्थायीकरण को यहां लागू नहीं किया गया है। मसलन, इसमें लाइव डेमो में , दो के बजाय केवल एक कंस्ट्रक्टर को लगाया जाता है। मेरा मानना ​​है कि C ++ मानक पुस्तकालय के कार्यान्वयन के कुछ आंतरिक कार्यान्वयन विवरण std::threadनिर्माणकर्ता के लिए लागू होने वाले अनुकूलन में बाधा डालते हैं ।

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