使用成员函数启动线程

abe*_*ier 269 c++ multithreading c++11

我试图std::thread用一个不带参数和返回的成员函数构造一个void.我无法弄清楚任何有效的语法 - 编译器无论如何都会抱怨.实现的正确方法是什么,spawn()以便返回std::thread执行的test()

#include <thread>
class blub {
  void test() {
  }
public:
  std::thread spawn() {
    return { test };
  }
};
Run Code Online (Sandbox Code Playgroud)

inf*_*inf 342

#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();
}
Run Code Online (Sandbox Code Playgroud)

编辑:计算你的编辑,你必须这样做:

  std::thread spawn() {
    return std::thread(&blub::test, this);
  }
Run Code Online (Sandbox Code Playgroud)

更新:我想解释一些问题,其中一些也在评论中讨论过.

上述语法是根据INVOKE定义(第20.8.2.1节)定义的:

定义INVOKE(f,t1,t2,...,tN)如下:

  • (t1.*f)(t2,...,tN)当f是指向类T的成员函数的指针时,t1是类型为T的对象或对类型为T的对象的引用或对类型T的引用源自T的类型的对象;
  • ((*t1).*f)(t2,...,tN)当f是指向类T的成员函数的指针时,t1不是前一项中描述的类型之一;
  • t1.*f当N == 1且f是指向类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));
Run Code Online (Sandbox Code Playgroud)

通过这样做,您承诺在线程对它们进行操作时,您将保证参数仍然存在.


请注意,上面提到的所有内容也可以应用于std::asyncstd::bind.

  • @LCID:`std :: thread`构造函数的多参数版本就像将参数传递给`std :: bind`一样.要调用成员函数,`std :: bind`的第一个参数必须是指向相应类型对象的指针,引用或共享指针. (15认同)
  • 请记住,不是静态成员函数作为第一个参数获取类的实例(它对程序员不可见),因此当将此方法作为原始函数传递时,在编译和声明不匹配期间始​​终会遇到问题. (4认同)
  • @KerrekSB,将[thread.thread.constr] p4与[func.bind.bind] p3进行比较,语义非常相似,根据INVOKE伪代码定义,定义如何调用成员函数 (3认同)

RnM*_*Mss 91

由于您使用的是C++ 11,因此lambda-expression是一个很好的清洁解决方案.

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

因为this->可以省略,它可以缩短为:

std::thread( [this] { test(); } )
Run Code Online (Sandbox Code Playgroud)

要不就

std::thread( [=] { test(); } )
Run Code Online (Sandbox Code Playgroud)

  • 通常,在按值返回局部变量时,不应使用`std :: move`.这实际上抑制了RVO.如果你只是按值返回(没有移动),编译器可能会使用RVO,如果没有,标准说它必须调用移动语义. (7认同)
  • RVO仍然生成比移动语义更好的代码,并且不会消失. (5认同)
  • 小心`[=]`.这样你就可以无意中复制一个巨大的物体.一般来说,使用`[&]`或`[=]`是*代码气味*. (2认同)
  • @Everyone别忘了这是一个主题.这意味着lambda函数可能比其上下文范围更长.因此,通过使用按引用捕获(`[&]`),您可能会引入一些类似悬空引用的错误.(例如,`std :: thread spawn(){int i = 10; return std :: thread([&] {std :: cout << i <<"\n";}};}`) (2认同)

小智 27

这是一个完整的例子

#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;
}
Run Code Online (Sandbox Code Playgroud)

使用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)
Run Code Online (Sandbox Code Playgroud)

  • 与OP问题没有多大关系,但为什么要在堆上分配Wrapper(而不是解除分配)?你有java/c#background吗? (10认同)
  • 没有理由在程序结束之前删除对象。不要无缘无故地让记忆羞辱人。 (3认同)
  • 不要忘记从堆中“删除”内存:) (2认同)

And*_*sev 14

@ hop5和@RnMss建议使用C++ 11 lambdas,但是如果你处理指针,你可以直接使用它们:

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出

2
Run Code Online (Sandbox Code Playgroud)

这个答案重写的样本将是:

#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;
}
Run Code Online (Sandbox Code Playgroud)