你所读的并不意味着你认为它意味着什么。首先,尝试shared_ptr本身的 msdn 页面。
向下滚动到“备注”部分,您将了解问题的实质。基本上,ashared_ptr<>
指向一个“控制块”,这是它如何跟踪shared_ptr<>
实际指向“真实”对象的对象数量。所以当你这样做时:
shared_ptr<int> ptr1 = make_shared<int>();
虽然这里只有 1 次调用来通过 分配内存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
,而不仅仅是从中读取。所以从多个线程写入一个对象是不好的,除非你已经通过锁保护它。global_instance
因此,您可以通过从中分配新对象来读取该对象shared_ptr<>
,但不能对其进行写入。
// 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()
{
// This is thread-safe and will work fine, though it's useless. Many
// short-lived pointers will be created and destroyed.
for(int i = 0; i < 10000; i++)
{
shared_ptr<int> temp = global_instance;
}
// This is not thread-safe. While all the threads are the same, the
// "final" value of this is almost certainly NOT going to be
// number_of_threads*10000 = 100,000. It'll be something else.
for(int i = 0; i < 10000; i++)
{
*global_instance = *global_instance + 1;
}
}
Ashared_ptr<>
是确保多个对象所有者确保对象被破坏的机制,而不是确保多个线程可以正确访问对象的机制。您仍然需要一个单独的同步机制才能在多个线程中安全地使用它(例如std::mutex)。
考虑它的最佳方式 IMO 是shared_ptr<>
确保指向同一内存的多个副本本身没有同步问题,但对指向的对象不做任何事情。就这样对待。