我是C ++新线程的新手,我正在尝试清楚地了解线程之间如何共享/不共享内存。我正在使用std::thread
和C ++ 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语法对我来说很新鲜。但希望我正在努力做的是连贯的。
这会像我期望的那样表现吗?如果没有,我误解了什么?
答案 0 :(得分:26)
记忆是记忆。 C ++中的对象占用内存中的某个位置;该位置可能位于堆栈上或堆上,也可能已静态分配。对象的位置无关紧要:任何具有引用或指向对象的指针的线程都可以访问该对象。如果两个线程有一个引用或指向该对象的指针,则两个线程都可以访问它。
在您的程序中,您创建一个工作线程(通过构造std::thread
)来执行您提供的lambda表达式。因为您通过引用捕获stackObj
和heapObj
(使用[&]
捕获默认值),所以lambda引用了这两个对象。
这些对象都位于主线程的堆栈上(注意heapObj
是一个指针类型的对象,它位于主线程的堆栈上,指向位于堆上的动态分配的对象)。没有制作这些物品的副本;相反,您的lambda表达式具有对象的引用。它直接修改stackObj
并间接修改heapObj
指向的对象。
主线程与工作线程连接后,heapObj->x
和stackObj.x
的值均为1
。
如果您使用了值捕获默认值([=]
),那么您的lambda表达式将复制 stackObj
和heapObj
。 lambda表达式中的表达式stackObj.x++
将增加副本,并且您在stackObj
中声明的main()
将保持不变。
如果按值捕获heapObj
,则仅复制指针本身,因此在使用指针的副本时,它仍然指向同一个动态分配的对象。表达式heapObj->x++
将取消引用该指针,产生您通过Obj
创建的new Obj()
,并递增其值。然后,您会在main()
结束时观察heapObj->x
已增加。
(注意,为了修改由value捕获的对象,必须将lambda表达式声明为mutable
。)
答案 1 :(得分:1)
我同意James McNellis heapObj->x
和stackObj.x
将1
。
此外,此代码仅有效,因为您在生成线程后立即join
。如果你启动了线程,然后在它运行时做了更多的工作,一个异常就可以展开堆栈,突然新线程stackObj
无效。这就是为什么在线程之间共享堆栈内存是一个坏主意,即使它在技术上是可行的。