向 std::thread 构造函数中的可调用对象, 或函数传递一个参数很简单。 需要注意的是, 默认参数要拷贝到线程独立内存中, 即使参数是引用的形式, 也可以在新线程中进行访问 。
void f(int i, std::string const& s);
std::thread t(f, 3, "hello");
代码创建了一个调用f(3, "hello")
的线程。 注意, 函数f需要一个 std::string 对象作为第二个参数, 但这里使用的是字符串的字面值, 也就是 char const *
类型。 之后, 在线程的上下文中完成字面值向 std::string 对象的转化。 需要特别要注意, 当指向动态变量的指针作为参数传递给线程的情况, 代码如下:
void f(int i,std::string const& s);
void oops(int some_param)
{
char buffer[1024]; // 1
sprintf(buffer, "%i",some_param);
std::thread t(f,3,buffer); // 2
t.detach();
}
这种情况下, buffer②是一个指针变量, 指向本地变量, 然后本地变量通过buffer传递到新线程中②。 并且, 函数有很有可能会在字面值转化成 std::string 对象之前崩溃(oops), 从而导致一些未定义的行为。 并且想要依赖隐式转换将字面值转换为函数期待的 std::string 对象,但因 std::thread 的构造函数会复制提供的变量, 就只复制了没有转换成期望类型的字符串字面值。
解决方案就是在传递到 std::thread 构造函数之前就将字面值转化为 std::string 对象:
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,std::string(buffer)); // 使用std::string, 避免悬垂指针
t.detach();
}
不过, 也有成功的情况: 复制一个引用。 在线程更新数据结构时, 会成功的传递一个引用:
void update_data_for_widget(widget_id w,widget_data& data); // 1
void oops_again(widget_id w)
{
widget_data data;
std::thread t(update_data_for_widget,w,data); // 2
display_status();
t.join();
process_widget_data(data); // 3
}
虽然update_data_for_widget
①的第二个参数期待传入一个引用, 但是 std::thread 的构造函数②并不知晓; 构造函数无视函数期待的参数类型, 并盲目的拷贝已提供的变量。 当线程调用update_data_for_widget
函数时, 传递给函数的参数是data变量内部拷贝的引用, 而非数据本身的引用。 因此, 当线程结束时, 内部拷贝数据将会在数据更新阶段被销毁, 且process_widget_data
将会接收到没有修改的data变量③。 可以使用 std::ref
将参数转换成引用的形式, 从而可将线程的调用改为以下形式:
std::thread t(update_data_for_widget,w,std::ref(data));
在这之后,
update_data_for_widget
就会接收到一个data变量的引用, 而非一个data变量拷贝的引用。