সদস্য ফাংশন দিয়ে থ্রেড শুরু করুন


294

আমি একটি গঠন করা চেষ্টা করছি std::threadএকজন সদস্য ফাংশন যা কোন যুক্তি এবং আয় লাগে সঙ্গে void। আমি কাজ করে এমন কোনও সিনট্যাক্স বের করতে পারি না - সংকলক যাই হোক না কেন অভিযোগ করে। বাস্তবায়ন সঠিক উপায় কি spawn()এটি একটি ফেরৎ যাতে std::threadযে, executes test()?

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

1
আপনার অর্থ কি ফাংশনটি শূন্য, যা অকার্যকর বলে প্রত্যাবর্তন করে বা এটির কোনও পরামিতি নেই। আপনি যা করার চেষ্টা করছেন তার জন্য আপনি কোড যুক্ত করতে পারেন?
জায়েদ আমির

আপনি পরীক্ষা করেছেন? (আমি এখনও করি নি)) আপনার কোডটি আরভিওর (রিটার্ন-ভ্যালু-অপ্টিমাইজেশন) এর উপর নির্ভর করে বলে মনে হচ্ছে, তবে আমার মনে হয় না আপনি এটি করার কথা বলেছিলেন। আমি মনে করি ব্যবহার std::move( std::thread(func) );করা ভাল, কারণ std::threadকোনও অনুলিপি-নির্মাণকারী নেই।
আরএনএমএস

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);
  }

আপডেট: আমি আরও কিছু বিষয় ব্যাখ্যা করতে চাই, তাদের কয়েকটি সম্পর্কে মন্তব্য করা হয়েছে।

উপরে বর্ণিত সিনট্যাক্সটি ইনভোক সংজ্ঞা (.820.8.2.1) এর শর্তে সংজ্ঞায়িত করা হয়েছে:

ইনভোক (এফ, টি 1, টি 2, ..., টিএন) সংজ্ঞায়িত করুন:

  • (t1। * f) (t2, ..., tN) যখন f টি শ্রেণি T এর সদস্য ফাংশনের পয়েন্টার হয় এবং টি 1 টি টাইপ টিয়ের একটি অবজেক্ট বা টাইপ টির কোনও অবজেক্টের রেফারেন্স বা একটি রেফারেন্স হয় টি থেকে প্রাপ্ত কোনও ধরণের অবজেক্ট;
  • ((* t1)। * f) (t2, ..., tN) যখন f টি শ্রেণীর টি এর সদস্য ফাংশনের পয়েন্টার হয় এবং টি 1 পূর্ববর্তী আইটেমটিতে বর্ণিত কোনও ধরণের নয়;
  • t1। * f যখন N == 1 এবং f টি শ্রেণি T এর সদস্য ডেটার পয়েন্টার হয় এবং t 1 টি টাইপ T এর
    একটি অবজেক্ট বা টাইপ T এর কোনও অবজেক্টের রেফারেন্স বা কোনও অবজেক্টের রেফারেন্স হয়
    টাইপ থেকে প্রাপ্ত টাইপের টি;
  • (* t1)। * f যখন N == 1 এবং f টি শ্রেণি T এর সদস্য ডেটার পয়েন্টার হয় এবং টি 1 পূর্ববর্তী আইটেমটিতে বর্ণিত কোনও ধরণের নয়;
  • অন্যান্য সমস্ত ক্ষেত্রে চ (টি 1, টি 2, ..., টিএন)।

আরেকটি সাধারণ সত্য যা আমি উল্লেখ করতে চাই তা হ'ল ডিফল্টরূপে থ্রেড কনস্ট্রাক্টর এতে প্রেরিত সমস্ত যুক্তি অনুলিপি করবে। এর কারণ হ'ল যুক্তিগুলির কলিং থ্রেডকে বহির্মুখী করা দরকার হতে পারে, আর্গুমেন্টগুলি অনুলিপি করে তার নিশ্চয়তা দেয়। পরিবর্তে, আপনি যদি সত্যিই একটি রেফারেন্স পাস করতে চান তবে আপনি এটি std::reference_wrapperদ্বারা তৈরি একটি ব্যবহার করতে পারেন std::ref

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

এটি করার মাধ্যমে, আপনি প্রতিশ্রুতি দিচ্ছেন যে থ্রেডটি যখন থ্রেডে চালিত হবে তখন আপনি তর্কগুলি এখনও বিদ্যমান থাকবে কিনা তা গ্যারান্টি দেওয়ার বিষয়ে যত্ন নেবেন।


দ্রষ্টব্য যে উপরে উল্লিখিত সমস্ত জিনিসগুলিতে std::asyncএবং প্রয়োগ করা যেতে পারে std::bind


1
কমপক্ষে এইভাবে এটি সংকলন করে। যদিও আপনি দ্বিতীয় যুক্তি হিসাবে আপনি উদাহরণটি পাস করছেন তা আমার কোনও ধারণা নেই।
অ্যাবারগমিয়ার

15
@ এলসিআইডি: কনস্ট্রাক্টরের মাল্টি-আর্গুমেন্ট ভার্সনটি std::threadএমনভাবে কাজ করে যেন আর্গুমেন্টগুলি পাস হয়েছে std::bind। সদস্য ফাংশনটি কল করতে, প্রথমে যুক্তিটি std::bindঅবশ্যই উপযুক্ত টাইপের কোনও অবজেক্টের জন্য পয়েন্টার, রেফারেন্স বা ভাগ করা পয়েন্টার হতে হবে।
ডেভ এস

কনস্ট্রাক্টর অন্তর্নিহিতের মতো কাজ করে এমন জায়গা থেকে আপনি এটি কোথায় নিয়েছেন bind? আমি কোথাও এটি খুঁজে পাচ্ছি না।
কেরেক এসবি

3
@ কেরেকএসবি, [থ্রেড.থ্রেড। কনট্রাস্ট] পি 4 এর সাথে [ফানক.বাইন্ড.বাইন্ড] পি 3 এর সাথে তুলনা করুন, শব্দার্থবিজ্ঞানগুলি একেবারে অনুরূপ, ইনভোক সিউডোকোডের পরিভাষায় সংজ্ঞায়িত হয়েছে, যা সদস্য ফাংশনগুলি কীভাবে বলা হয় তা সংজ্ঞায়িত করে
জোনাথন ওয়েকেলি

4
মনে রাখবেন যে প্রথম প্যারামিটার হিসাবে স্থির সদস্যের ফাংশন ক্লাসের উদাহরণ না নেয় (এটি প্রোগ্রামারের জন্য দৃশ্যমান নয়), সুতরাং এই পদ্ধতিটি কাঁচা ফাংশন হিসাবে পাস করার সময় আপনি সর্বদা সংকলন এবং ঘোষণার অমিলের সময় কোনও সমস্যার মুখোমুখি হবেন।
জোস্কা

100

যেহেতু আপনি সি ++ 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মান অনুসারে কোনও স্থানীয় ভেরিয়েবল ফিরিয়ে দেওয়ার সময় আপনার ব্যবহার করা উচিত নয় । এটি আসলে আরভিওকে বাধা দেয়। আপনি যদি কেবল মূল্য দিয়ে ফিরে যান (সরানো ব্যতীত) সংকলকটি আরভিও ব্যবহার করতে পারে, এবং যদি এটি মান না বলে তবে এটি সরানো শব্দার্থক শব্দটি আহ্বান করতে হবে।
zmb

@Zmb, আপনি ভিসি 10 তে সংকলন করতে কোডটি চান এর ব্যতিক্রম সহ, আপনি যদি রিটার্নের ধরণটি কপি কনস্ট্রাকটেবল না করে থাকেন তবে আপনাকে স্থানান্তর করতে হবে।
abergmeier

6
আরভিও এখনও শব্দার্থক পদক্ষেপের চেয়ে আরও ভাল কোড উত্পন্ন করে এবং চলে যাচ্ছে না।
জোনাথন ওয়েকেলি

2
সঙ্গে সাবধান [=]। এটির সাহায্যে আপনি অজান্তে একটি বিশাল বস্তু অনুলিপি করতে পারেন। সাধারণভাবে, এটি ব্যবহারের জন্য একটি কোড গন্ধ[&] বা [=]
rustyx

3
@ এভেরিওন এটি এখানে একটি থ্রেড ভুলবেন না। এর অর্থ ল্যাম্বডা ফাংশনটি তার প্রসঙ্গের সুযোগটি ছাড়িয়ে যেতে পারে। সুতরাং ক্যাপচার-বাই-রেফারেন্স ( [&]) ব্যবহার করে আপনি কিছু ঝুলন্ত রেফারেন্সের মতো বাগ প্রবর্তন করতে পারেন। (উদাহরণস্বরূপ, std::thread spawn() { int i = 10; return std::thread( [&] { std::cout<<i<<"\n"; } ); })
আরএনএমএস

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++ -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
ওপি প্রশ্নের সাথে সত্যই প্রাসঙ্গিক নয়, তবে কেন আপনি গাদা র‍্যাপারকে বরাদ্দ করেন (এবং এটি হ্রাস করবেন না)? আপনার জাভা / সি # ব্যাকগ্রাউন্ড আছে?
আলেসান্দ্রো তেরুজি

deleteগাদা থেকে স্মৃতিটি ভুলে যাবেন না :)
স্ল্যাক বট

19

@ হপ 5 এবং @ আরএনএমএস সি ++ ১১ টি ল্যাম্বডাস ব্যবহার করার পরামর্শ দিয়েছিল, তবে আপনি যদি পয়েন্টারগুলি ব্যবহার করেন তবে আপনি সেগুলি সরাসরি ব্যবহার করতে পারেন:

#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();
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.