सदस्य फ़ंक्शन के साथ थ्रेड प्रारंभ करें


294

मैं std::threadएक सदस्य फ़ंक्शन के साथ निर्माण करने की कोशिश कर रहा हूं जो कोई तर्क और रिटर्न नहीं लेता है void। मैं किसी भी सिंटैक्स का पता नहीं लगा सकता जो काम करता है - संकलक शिकायत करता है कि कोई बात नहीं क्या। spawn()इसे लागू करने का सही तरीका क्या है ताकि यह std::threadनिष्पादित हो जाए test()?

#include <thread>
class blub {
  void test() {
  }
public:
  std::thread spawn() {
    return { test };
  }
};

1
क्या इसका मतलब यह है कि फ़ंक्शन शून्य कहलाता है, इसे शून्य कहा जाता है या इसके पास कोई पैरामीटर नहीं है। क्या आप जो करने की कोशिश कर रहे हैं, क्या आप उसके लिए कोड जोड़ सकते हैं?
ज़ैद अमीर

क्या आपने परीक्षण किया है? (मैंने अभी तक नहीं किया है।) आपका कोड आरवीओ (रिटर्न-वैल्यू-ऑप्टिमाइजेशन) पर भरोसा करने लगता है, लेकिन मुझे नहीं लगता कि आप ऐसा करने वाले हैं। मुझे लगता है कि इसका उपयोग std::move( std::thread(func) );करना बेहतर है, क्योंकि std::threadइसमें कॉपी-कंस्ट्रक्टर नहीं है।
RnMss

4
@ आरएनएमएसएस: आप आरवीओ पर भरोसा कर सकते हैं , std::moveइस मामले में उपयोग बेमानी है - क्या यह सच नहीं था, और कोई कॉपी कंस्ट्रक्टर नहीं था, कंपाइलर वैसे भी एक त्रुटि देगा।
क्वालिया

जवाबों:


367
#include <thread>
#include <iostream>

class bar {
public:
  void foo() {
    std::cout << "hello from member function" << std::endl;
  }
};

int main()
{
  std::thread t(&bar::foo, bar());
  t.join();
}

संपादित करें: अपने संपादन का लेखा-जोखा, आपको इसे इस तरह करना होगा:

  std::thread spawn() {
    return std::thread(&blub::test, this);
  }

अद्यतन: मैं कुछ और बिंदुओं की व्याख्या करना चाहता हूं, उनमें से कुछ पर टिप्पणियों में भी चर्चा की गई है।

ऊपर वर्णित सिंटैक्स को INVOKE परिभाषा (.220.8.2.1) के संदर्भ में परिभाषित किया गया है:

निम्न के अनुसार INVOKE (f, t1, t2, ..., tN) को परिभाषित करें:

  • (t1। * f) (t2, ..., tN) जब f एक वर्ग T के सदस्य फ़ंक्शन का सूचक है और t1 टाइप T का एक ऑब्जेक्ट है या टाइप T के ऑब्जेक्ट का संदर्भ या a का संदर्भ है टी से प्राप्त एक प्रकार की वस्तु;
  • ((* t1)। * f) (t2, ..., tN) जब f एक वर्ग T के सदस्य फ़ंक्शन के लिए एक सूचक है और t1 पिछले आइटम में वर्णित प्रकारों में से एक नहीं है;
  • t1। * f जब N == 1 और f एक वर्ग T के सदस्य डेटा के लिए एक सूचक है T और t 1 टाइप T
    का एक ऑब्जेक्ट है या टाइप T के ऑब्जेक्ट का संदर्भ या किसी ऑब्जेक्ट के ऑब्जेक्ट का संदर्भ है
    व्युत्पन्न प्रकार के । टी;
  • (* t1)। * f जब N == 1 और f एक वर्ग T के सदस्य डेटा का सूचक है और t 1, पिछले आइटम में वर्णित प्रकारों में से एक नहीं है;
  • f (t1, t2, ..., tN) अन्य सभी मामलों में।

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

std::thread (foo, std::ref(arg1));

ऐसा करने से, आप वादा कर रहे हैं कि आप इस बात की गारंटी लेंगे कि जब धागा उन पर काम करेगा तब भी तर्क मौजूद रहेंगे।


ध्यान दें कि ऊपर बताई गई सभी चीज़ों पर भी लागू किया जा सकता है std::asyncऔर std::bind


1
कम से कम इस तरह से यह संकलन करता है। हालांकि मुझे इस बात का कोई अंदाजा नहीं है कि आप दूसरे तर्क के रूप में उदाहरण क्यों दे रहे हैं।
abergmeier

15
@LCID: कंस्ट्रक्टर का बहु-तर्क संस्करण std::threadकाम करता है जैसे कि तर्कों को पारित किया गया था std::bind। सदस्य फ़ंक्शन को कॉल करने std::bindके लिए, उपयुक्त प्रकार के ऑब्जेक्ट के लिए सूचक, संदर्भ या साझा किए गए पॉइंटर का पहला तर्क होना चाहिए।
डेव एस

आप इसे कहां से लेते हैं कि निर्माता एक अंतर्निहित कार्य करता है bind? मुझे वह कहीं नहीं मिलेगा।
केरेक एसबी

3
@KerrekSB, [thread.thread.constr] p4 की तुलना [func.bind.bind] p3 के साथ करें, शब्दार्थ काफी समान हैं, इन्हें INVOKE स्यूडोकोड के संदर्भ में परिभाषित किया गया है, जो इस बात को परिभाषित करते हैं कि कैसे सदस्य कार्य कहलाते हैं
जोनाथन वेकली

4
याद रखें कि पहले पैरामीटर के रूप में स्थैतिक सदस्य कार्य नहीं करते हैं (उदाहरण के लिए यह प्रोग्रामर के लिए दिखाई नहीं देता है) कक्षा का उदाहरण लेते हैं, इसलिए जब कच्चे फ़ंक्शन के रूप में इस पद्धति को पारित करते हैं तो आप हमेशा संकलन और घोषणा बेमेल के दौरान समस्या का सामना करेंगे।
ज़ोस्का

100

जब से आप C ++ 11 का उपयोग कर रहे हैं, लैम्बडा-एक्सप्रेशन एक अच्छा और साफ समाधान है।

class blub {
    void test() {}
  public:
    std::thread spawn() {
      return std::thread( [this] { this->test(); } );
    }
};

चूंकि this->छोड़ा जा सकता है, इसलिए इसे छोटा किया जा सकता है:

std::thread( [this] { test(); } )

या केवल

std::thread( [=] { test(); } )

8
सामान्य तौर पर, std::moveमूल्य द्वारा स्थानीय चर वापस करते समय आपको इसका उपयोग नहीं करना चाहिए । यह वास्तव में RVO को रोकता है। यदि आप केवल मूल्य (चाल के बिना) पर लौटते हैं, तो कंपाइलर आरवीओ का उपयोग कर सकता है, और यदि यह मानक नहीं कहता है तो उसे चाल शब्दार्थ को लागू करना होगा।
zmb

@zmb, उस अपवाद के साथ जिसे आप चाहते हैं कि कोड को VC10 पर संकलित किया जाए, आपको यह स्थानांतरित करना होगा कि क्या रिटर्न प्रकार CopyCraftstructable नहीं है।
abergmeier

6
आरवीओ अभी भी चाल शब्दार्थ से बेहतर कोड उत्पन्न करता है, और दूर नहीं जा रहा है।
जोनाथन वैक्ली

2
से सावधान रहें [=]। इससे आप अनजाने में एक बड़ी वस्तु की नकल कर सकते हैं। सामान्य तौर पर, यह उपयोग करने के लिए या कोड गंध है । [&][=]
रस्टेक्स

3
@ हर कोई यह एक धागा यहाँ मत भूलना। इसका मतलब यह है कि लंबो फंक्शन अपने संदर्भ दायरे से आगे निकल सकता है। इसलिए कैप्चरिंग-बाय-रेफरेंस ( [&]) का उपयोग करके , आप कुछ झूलने वाले संदर्भों की तरह बग का परिचय दे सकते हैं। (उदाहरण के लिए std::thread spawn() { int i = 10; return std::thread( [&] { std::cout<<i<<"\n"; } ); })
RnMss

29

यहाँ एक पूर्ण उदाहरण है

#include <thread>
#include <iostream>

class Wrapper {
   public:
      void member1() {
          std::cout << "i am member1" << std::endl;
      }
      void member2(const char *arg1, unsigned arg2) {
          std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
      }
      std::thread member1Thread() {
          return std::thread([=] { member1(); });
      }
      std::thread member2Thread(const char *arg1, unsigned arg2) {
          return std::thread([=] { member2(arg1, arg2); });
      }
};
int main(int argc, char **argv) {
   Wrapper *w = new Wrapper();
   std::thread tw1 = w->member1Thread();
   std::thread tw2 = w->member2Thread("hello", 100);
   tw1.join();
   tw2.join();
   return 0;
}

G ++ के साथ संकलन करने से निम्नलिखित परिणाम प्राप्त होते हैं

g++ -Wall -std=c++11 hello.cc -o hello -pthread

i am member1
i am member2 and my first arg is (hello) and second arg is (100)

10
वास्तव में ओपी प्रश्न के लिए प्रासंगिक नहीं है, लेकिन आप रैपर को ढेर पर क्यों आवंटित करते हैं (और इसे निपटाएं नहीं)? क्या आपके पास java / c # बैकग्राउंड है?
एलेसेंड्रो टेरुज़ी

deleteस्मृति को ढेर से मत भूलना :)
स्लैक बॉट

19

@ hop5 और @RnMss ने C ++ 11 लैम्ब्डा का उपयोग करने का सुझाव दिया, लेकिन यदि आप पॉइंटर्स से निपटते हैं, तो आप सीधे उपयोग कर सकते हैं:

#include <thread>
#include <iostream>

class CFoo {
  public:
    int m_i = 0;
    void bar() {
      ++m_i;
    }
};

int main() {
  CFoo foo;
  std::thread t1(&CFoo::bar, &foo);
  t1.join();
  std::thread t2(&CFoo::bar, &foo);
  t2.join();
  std::cout << foo.m_i << std::endl;
  return 0;
}

आउटपुट

2

इस जवाब से फिर से लिखा नमूना होगा:

#include <thread>
#include <iostream>

class Wrapper {
  public:
      void member1() {
          std::cout << "i am member1" << std::endl;
      }
      void member2(const char *arg1, unsigned arg2) {
          std::cout << "i am member2 and my first arg is (" << arg1 << ") and second arg is (" << arg2 << ")" << std::endl;
      }
      std::thread member1Thread() {
          return std::thread(&Wrapper::member1, this);
      }
      std::thread member2Thread(const char *arg1, unsigned arg2) {
          return std::thread(&Wrapper::member2, this, arg1, arg2);
      }
};

int main() {
  Wrapper *w = new Wrapper();
  std::thread tw1 = w->member1Thread();
  tw1.join();
  std::thread tw2 = w->member2Thread("hello", 100);
  tw2.join();
  return 0;
}

0

कुछ उपयोगकर्ताओं ने पहले ही अपना जवाब दे दिया है और इसे बहुत अच्छी तरह से समझाया है।

मैं थ्रेड से जुड़ी कुछ और चीजें जोड़ना चाहूंगा।

  1. फन्ने और थ्रेड के साथ कैसे काम करें। कृपया नीचे दिए गए उदाहरण को देखें।

  2. थ्रेड ऑब्जेक्ट को पास करते समय ऑब्जेक्ट की अपनी प्रतिलिपि बना देगा।

    #include<thread>
    #include<Windows.h>
    #include<iostream>
    
    using namespace std;
    
    class CB
    {
    
    public:
        CB()
        {
            cout << "this=" << this << endl;
        }
        void operator()();
    };
    
    void CB::operator()()
    {
        cout << "this=" << this << endl;
        for (int i = 0; i < 5; i++)
        {
            cout << "CB()=" << i << endl;
            Sleep(1000);
        }
    }
    
    void main()
    {
        CB obj;     // please note the address of obj.
    
        thread t(obj); // here obj will be passed by value 
                       //i.e. thread will make it own local copy of it.
                        // we can confirm it by matching the address of
                        //object printed in the constructor
                        // and address of the obj printed in the function
    
        t.join();
    }

उसी चीज को प्राप्त करने का एक और तरीका है:

void main()
{
    thread t((CB()));

    t.join();
}

लेकिन अगर आप ऑब्जेक्ट को संदर्भ से पास करना चाहते हैं तो नीचे दिए गए सिंटैक्स का उपयोग करें:

void main()
{
    CB obj;
    //thread t(obj);
    thread t(std::ref(obj));
    t.join();
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.