多线程传参的注意事项

void f(int i,std::string const& s);
void not_oops(int some_param)
{
  char buffer[1024];
  sprintf(buffer,"%i",some_param);
  std::thread t(f,3,buffer);
  t.detach();
}

1、考虑std::thread t(f,3,buffer);这句话,当f的第二个参数为一个字符串常量时,没有问题,当它是一个变量的指针时,即例子中的buffer,有可能buffer还没有完全转换为std::string的时候函数not_oops已经返回退出了,原因是std::thread的构造函数按照原来的样子拷贝所传的参数,并不会转换参数的类型。

2、特别的,当函数的参数为非常量引用时,std::thread的构造函数并不知道函数想要的参数是引用,它并不在意函数所期望的参数类型,而只是盲目地拷贝所传的值,但是为了与move-only类型共事,内部代码把拷贝的参数作为右值来传递,这将引发编译错误,因为不能将右值传递给一个期望非常量引用的函数,解决方法是使用std::ref(param)的形式传参。

3、只要提供合适的对象指针作为函数的第一个参数,就可以传递这个对象的一个成员函数的指针作为线程中要执行的函数,例如:

class X
{
public:
void do_lengthy_work();
};
X my_x;
std::thread t(&X::do_lengthy_work,&my_x);

等价于在新线程中调用my_x.do_lengthy_work()。如果成员函数带有参数,则std::thread构造函数的第三个参数将会是这个成员函数的参数。

4、参数不能被拷贝只能被移动的情形。这中类型的一个例子是std::unique_ptr,当源对象是临时的,移动将自动进行,但是当源对象是一个被命名的值时,就需要显示调用std::move()来进行移动。例如:

void process_big_object(std::unique_ptr<big_object>);
std::unique_ptr<big_object> p(new big_object);
p->prepare_data(42);
std::thread t(process_big_object,std::move(p));

在std::thread的构造函数中使用std::move,big_object的所有权先是转移到了新建线程的内部存储,然后再转移到process_big_object。尽管std::thread的实例不像std::unique_ptr那样拥有动态对象,std::thread的每个实例负责管理一个线程的执行,这个所有权可以在std::thread的实例之间进行转移,这确保了在同一时间只有一个对象和一个特定的执行线程相关联。

posted @ 2023-05-18 16:32  许卡文迪  阅读(15)  评论(0编辑  收藏  举报