22

我是 C++ 线程的新手,我试图清楚地了解内存是如何在线程之间共享/不共享的。我正在使用std::threadC++11。根据我在其他 SO 问题上所读到的内容,堆栈内存仅由一个线程拥有,而内存在线程之间共享。因此,根据我对堆栈与堆的理解,以下应该是正确的:

#include <thread>
using namespace std;

class Obj {
public:
    int x;
    Obj(){x = 0;}
};

int main() {
    Obj stackObj;
    Obj *heapObj = new Obj();
    thread t([&]{
        stackObj.x++;
        heapObj->x++;
    });
    t.join();
    assert(heapObj->x == 1);
    assert(stackObj.x == 0);
}

如果我搞砸了一堆东西,请原谅我,lambda 语法对我来说很新。但希望我正在尝试做的是连贯的。这会按我的预期执行吗?如果不是,我有什么误解?

4

2 回答 2

29

记忆就是记忆。C++ 中的对象占用内存中的某个位置;该位置可能在堆栈或堆上,或者它可能已被静态分配。对象的位置无关紧要:任何具有对象引用或指针的线程都可以访问该对象。如果两个线程有​​一个对象的引用或指针,那么两个线程都可以访问它。

在您的程序中,您创建一个工作线程(通过构造一个std::thread)来执行您提供的 lambda 表达式。因为您通过引用捕获两者stackObjheapObj使用[&]捕获默认值),所以该 lambda 具有对这两个对象的引用。

这些对象都位于主线程的堆栈上(请注意,这heapObj是一个指针类型的对象,位于主线程的堆栈上,并指向位于堆上的动态分配的对象)。不制作这些对象的副本;相反,您的 lambda 表达式具有对对象的引用。它直接修改和修改间接stackObj指向的对象。heapObj

主线程加入工作线程后,两者heapObj->xstackObj.x值都为1.


如果您使用了值捕获默认值 ( [=]),您的 lambda 表达式将同时复制stackObjheapObj。lambda表达式stackObj.x++中的表达式将增加copy,而stackObj您在其中声明的main()将保持不变。

如果捕获heapObj按值,则仅复制指针本身,因此在使用指针的副本时,它仍然指向同一个动态分配的对象。该表达式heapObj->x++将取消引用该指针,产生Obj您通过 创建的new Obj(),并增加其值。然后,您会在末尾观察到main()heapObj->x递增。

(请注意,为了修改按值捕获的对象,必须声明 lambda 表达式mutable。)

于 2012-07-05T20:44:11.787 回答
1

我同意 James McNellis 的观点,heapObj->x并且stackObj.x将是1

此外,此代码在您join生成线程后立即起作用。如果你启动了线程,然后在它运行时做了更多的工作,异常可能会展开堆栈,突然新线程stackObj无效。这就是为什么在线程之间共享堆栈内存是一个坏主意,即使它在技术上是可行的。

于 2012-07-05T20:47:48.960 回答