你所读的内容并不意味着你所想的。首先,请参阅
shared_ptr页面的“Remarks”部分。
向下滚动,你会找到问题的核心。基本上,
shared_ptr<>
指向一个“控制块”,这是它跟踪多少
shared_ptr<>
对象实际上指向“真实”对象的方式。所以当你这样做时:
shared_ptr<int> ptr1 = make_shared<int>();
尽管只有一个调用make_shared
来分配内存,但有两个“逻辑”块,您不应该将它们视为相同。其中一个是存储实际值的int
,另一个是控制块,它存储使其工作的所有shared_ptr<>
“魔法”。
仅控制块本身是线程安全的。
我将其放在了单独的一行以突出显示。shared_ptr
的内容和写入同一个shared_ptr
实例都不是线程安全的。以下是演示我的意思的内容:
// In main()
shared_ptr<myClass> global_instance = make_shared<myClass>();
// (launch all other threads AFTER global_instance is fully constructed)
//In thread 1
shared_ptr<myClass> local_instance = global_instance;
事实上,您可以在所有线程中尽情这样做。 当local_instance
被销毁(超出范围)时,它也是线程安全的。 可以访问global_instance
,不会有影响。 你从msdn中提取的代码段基本上意味着“对控制块的访问是线程安全的”,因此可以在不同的线程上创建和销毁其他shared_ptr<>
实例,只要需要。
//In thread 1
local_instance = make_shared<myClass>();
这很好。它会影响global_instance
对象,但只是间接地。它指向的控制块将以线程安全的方式递减。local_instance
将不再指向与global_instance
相同的对象(或控制块)。
//In thread 2
global_instance = make_shared<myClass>();
如果您从其他线程访问 global_instance
(正如您所说的那样),那么几乎可以肯定这是不好的。如果您这样做,它需要一个锁,因为您正在写入到 global_instance
所在的位置,而不仅仅是从中读取。因此,除非通过锁定对其进行了保护,否则从多个线程写入对象是不好的。因此,您可以通过将新的shared_ptr<>
对象分配给它来从 global_instance
中读取对象,但无法对其进行写入。
// In thread 3
*global_instance = 3;
int a = *global_instance;
// In thread 4
*global_instance = 7;
a
的值是未定义的。它可能是 7,也可能是 3,或者可能是其他任何值。 shared_ptr<>
实例的线程安全仅适用于管理从彼此初始化的 shared_ptr<>
实例,而不是它们所指向的内容。
为了强调我的意思,请看这个例子:
shared_ptr<int> global_instance = make_shared<int>(0);
void thread_fcn();
int main(int argc, char** argv)
{
thread thread1(thread_fcn);
thread thread2(thread_fcn);
...
thread thread10(thread_fcn);
chrono::milliseconds duration(10000);
this_thread::sleep_for(duration);
return;
}
void thread_fcn()
{
for(int i = 0; i < 10000; i++)
{
shared_ptr<int> temp = global_instance;
}
for(int i = 0; i < 10000; i++)
{
*global_instance = *global_instance + 1;
}
}
shared_ptr<>
是一种机制,用于确保多个对象所有者确保对象被销毁,而不是用于确保多个线程可以正确访问一个对象。您仍需要单独的同步机制才能在多个线程中安全使用它(例如std::mutex)。
我认为最好的理解方式是,shared_ptr<>
确保指向同一内存的多个副本没有自身的同步问题,但对所指向的对象没有任何作用。 把它当作这样处理。
private
是一个关键字,不能用作变量名。 - M.M