指针和引用之间的差异作为线程参数

时间:2011-02-25 11:36:07

标签: c++ multithreading c++11 reference stdthread

这是一个例子:

#include<iostream>
#include<thread>
using namespace std;

void f1(double& ret) {
   ret=5.;
}

void f2(double* ret) {
   *ret=5.;
}

int main() {
   double ret=0.;
   thread t1(f1, ret);
   t1.join();
   cout << "ret=" << ret << endl;
   thread t2(f2, &ret);
   t2.join();
   cout << "ret=" << ret << endl;   
}

输出是:

ret=0
ret=5

使用gcc 4.5.2编译,带有和不带-O2标记。

这是预期的行为吗?

这个节目数据是否免费比赛?

谢谢

3 个答案:

答案 0 :(得分:83)

std::thread的构造函数推导出参数类型并按值存储它们的副本。这需要确保参数对象的生命周期至少与线程的生命周期相同。

C ++模板函数参数类型推导机制从类型T的参数中推导出类型T&。将复制std::thread的所有参数,然后将其传递给线程函数,以便f1()f2()始终使用该副本。

如果您坚持使用引用,请使用boost::ref()std::ref()包装参数:

thread t1(f1, boost::ref(ret));

或者,如果您更喜欢简单,请传递指针。这是boost::ref()std::ref()在幕后为您做的事情。

答案 1 :(得分:9)

在这些情况下,您需要明确的std::ref()(或boost::ref())实际上是一个非常有用的安全功能,因为传递引用本质上是一件危险的事情。

使用非const引用时,通常存在传递局部变量的危险,使用const引用它可能是临时的,并且正在创建要在不同线程中调用的函数(并且通常使用绑定,通常是稍后/以异步方式调用的函数),您将面临对象不再有效的巨大危险。

绑定看起来很整洁,但这些错误是最难找到的,因为错误被捕获(即在调用函数时)与错误的位置不同(在绑定时)并且它可能非常很难确切地确定当时正在调用哪个函数,因此它被绑定的位置。

在您作为参考传递的变量范围内加入线程时,在您的实例中是安全的。因此,当您知道这种情况时,就会有一种传递引用的机制。

这不是我想要改变的语言的一个特征,特别是因为可能有很多现有的代码依赖于它制作一个副本,如果它只是自动引用就会破坏(然后需要一个强制复制的明确方法。

答案 2 :(得分:7)

如果您想通过引用std::thread传递参数,则必须将每个参数括在std::ref中:

thread t1(f1, std::ref(ret));

更多信息here