38

这是一个例子:

#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标志。

这是预期的行为吗?

这个程序数据竞赛是免费的吗?

谢谢

4

3 回答 3

88

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

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

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

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

或者,如果您更喜欢简单,请传递一个指针。这就是在幕后为你做什么boost::ref()或做什么。std::ref()

于 2011-02-25T11:55:10.860 回答
9

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

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

更多信息在这里

于 2011-02-25T11:53:14.130 回答
9

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

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

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

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

这不是我希望看到更改的语言的一个功能,特别是因为可能有很多现有代码依赖它制作一个副本,如果它只是通过引用自动获取就会破坏(然后需要一种明确的方式来强制复制)。

于 2011-02-25T12:51:04.537 回答