メンバー関数でスレッドを開始


294

std::thread引数を取らずに返すメンバー関数を使ってを作成しようとしていますvoid。機能する構文を理解できません。コンパイラは何があっても文句を言います。実行するspawn()a std::threadを返すように実装する正しい方法は何test()ですか?

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

1
関数がvoidと呼ばれるvoidを返すことを意味しますか、それともパラメーターがありません。あなたがやろうとしていることのためのコードを追加できますか?
Zaid Amir

テストしましたか?(私はまだです。)あなたのコードはRVO(return-value-optimzation)に依存しているようですが、そうする必要があるとは思いません。コピーコンストラクタがないstd::move( std::thread(func) );ため、使用する方が良いと思いますstd::thread
RnMss 2013年

4
@RnMss:RVO を信頼できstd::moveます。この場合、使用は冗長です-これが当てはまらず、コピーコンストラクターがなかった場合、コンパイラーはとにかくエラーを出します。
Qualia

回答:


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定義(20.8.2.1)の観点から定義されています。

INVOKE(f、t1、t2、...、tN)を次のように定義します。

  • (t1。* f)(t2、...、tN)fがクラスTのメンバー関数へのポインターであり、t1がタイプTのオブジェクトまたはタイプTのオブジェクトへの参照またはタイプへの参照である場合Tから派生した型のオブジェクト。
  • ((* t1)。* f)(t2、...、tN)fがクラスTのメンバー関数へのポインターであり、t1が前の項目で説明した型の1つでない場合。
  • N == 1であり、fがクラスTのメンバーデータへのポインターであり、t 1がT型
    のオブジェクトであるか、T型のオブジェクトへの参照であるか、またはT
    から派生した型のオブジェクトへの参照である場合のt1。* f T;
  • (* t1)。* f N == 1で、fがクラスTのメンバーデータへのポインターで、t 1が前の項目で説明した型の1つでない場合。
  • その他の場合はすべてf(t1、t2、...、tN)。

私が指摘したいもう1つの一般的な事実は、デフォルトでは、スレッドコンストラクターは渡されたすべての引数をコピーするということです。これは、引数が呼び出しスレッドよりも長く存続する必要がある可能性があるためです。引数をコピーすると保証されます。代わりに、本当に参照を渡したい場合は、std::reference_wrappercreated byを使用できますstd::ref

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

これを行うことにより、スレッドが引数を操作するときに引数が存在することを保証することを約束します。


上記のすべてのものをも適用することができるstd::asyncstd::bind


1
少なくともこの方法でコンパイルされます。なぜインスタンスを2番目の引数として渡すのかはわかりませんが。
abergmeier、2012年

15
@LCID:コンストラクタの複数引数バージョンはstd::thread、引数がに渡されたかのように機能しますstd::bind。メンバー関数を呼び出すには、への最初の引数std::bindは、適切な型のオブジェクトへのポインター、参照、または共有ポインターでなければなりません。
Dave S

コンストラクタは暗黙のように振る舞うということから、どこからそれを取りbindますか?それはどこにもありません。
Kerrek SB、2012

3
@KerrekSB、[thread.thread.constr] p4と[func.bind.bind] p3を比較すると、セマンティクスは非常に似ており、メンバー関数の呼び出し方法を定義するINVOKE疑似コードで定義されています
Jonathan Wakely

4
最初のパラメーターとして静的メンバー関数はクラスのインスタンスをとらないことに注意してください(プログラマーからは見えません)。このメソッドをraw関数として渡すと、コンパイルと宣言の不一致時に常に問題が発生します。
zoska 2013年

100

C ++ 11を使用しているので、lambda-expressionは素晴らしいクリーンなソリューションです。

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を阻害します。値で(移動せずに)返すだけの場合、コンパイラーはRVOを使用する場合があり、標準に準拠していない場合は、移動のセマンティクスを呼び出さなければならないと標準で規定されています。
zmb

@ zmb、VC10でコードをコンパイルすることを除いて、戻り値の型がCopyConstructableでない場合は移動する必要があります。
abergmeier 2013年

6
RVOは、移動のセマンティクスよりも優れたコードを生成し、なくなることはありません。
Jonathan Wakely 2014年

2
に注意してください[=]。これにより、誤って巨大なオブジェクトをコピーしてしまう可能性があります。一般的には、やを使用するコードのにおいです。[&][=]
rustyx 2016

3
@Everyoneここがスレッドであることを忘れないでください。つまり、ラムダ関数はそのコンテキストスコープよりも長く存続する可能性があります。したがって、参照によるキャプチャ([&])を使用すると、ぶら下がる参照などのバグが発生する可能性があります。(例:std::thread spawn() { int i = 10; return std::thread( [&] { std::cout<<i<<"\n"; } ); }
RnMss 2018年

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
OPの質問にはあまり関係ありませんが、なぜWrapperをヒープに割り当てます(割り当てを解除しないのですか)。あなたはjava / c#の背景を持っていますか?
Alessandro Teruzzi

deleteヒープからのメモリを忘れないでください:)
Slack Bot

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();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.