【问题标题】:Difference between pointer and reference as thread parameter指针和引用作为线程参数的区别
【发布时间】:2011-07-04 06:11:03
【问题描述】:

这是一个例子:

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

这是预期的行为吗?

这个程序数据竞争免费吗?

谢谢

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

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

    【讨论】:

    • 这对我很有帮助,谢谢+1。如果这条消息仍然会被任何人注意到,请问thread使用这种机制的原因。这和c有关系,还是完全不同的原因?
    • @patrik 值语义(复制)与存储引用或指针相比是安全的。当它们引用的对象被销毁时,引用或指针可能会变得悬空。这就是为什么 std::bindstd::thread 默认按值存储参数并且您必须明确要求它做不那么安全的事情。
    【解决方案2】:

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

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

    更多信息here

    【讨论】:

      【解决方案3】:

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2010-10-11
        • 2021-02-02
        • 2015-02-07
        • 2010-12-26
        • 2011-09-22
        • 1970-01-01
        • 2014-04-21
        • 1970-01-01
        • 2016-02-03
        相关资源
        最近更新 更多