您所阅读的内容并不代表您认为的意思。首先,尝试 shared_ptr 本身的 msdn 页面。
向下滚动到“备注”部分,您将了解问题的实质。基本上,shared_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 中提取的 sn-p 基本上意味着“对控制块的访问是线程安全的”,因此可以根据需要在不同的线程上创建和销毁其他 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()
{
// 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;
}
}
shared_ptr<> 是一种确保多个对象所有者 确保一个对象被破坏的机制,而不是一种确保多个线程 可以正确访问一个对象的机制。你仍然需要一个单独的同步机制来在多个线程中安全地使用它(比如std::mutex)。
考虑它的最佳方式 IMO 是 shared_ptr<> 确保指向同一内存的多个副本对于自身没有同步问题,但不会为指向的对象。就这样对待吧。