std :: shared_ptr线程安全
我读过了
即使对象是共享所有权的副本,多个线程也可以同时读写不同的shared_ptr对象。 ( MSDN:标准C ++库中的线程安全 )
这是否意味着更改shared_ptr对象是安全的?
例如,下一个代码是否被认为是安全的:
shared_ptr<myClass> global = make_shared<myClass>(); ... //In thread 1 shared_ptr<myClass> private = global; ... //In thread 2 global = make_shared<myClass>(); ...
我可以肯定,在这种情况下,线程1 private
将具有global
的原始值或线程2分配的新值,但无论哪种方式将有一个有效的shared_ptr myClass?
== ==编辑
只是为了解释我的动机。 我想有一个共享的指针来保存我的configuration,我有一个线程池来处理请求。
所以global
是全球configuration。
thread 1
正在采取当前configuration,因为它开始处理请求。
thread 2
正在更新configuration。 (只适用于未来的请求)
如果工作的话,我可以在不中断请求处理的情况下更新configuration。
你正在阅读的内容并不意味着你的想法。 首先,尝试使用shared_ptr本身的msdn页面。
向下滚动到“备注”部分,你会发现问题的关键。 基本上, shared_ptr<>
指向一个“控制块”,它是如何跟踪有多lessshared_ptr<>
对象实际上指向“Real”对象。 所以当你这样做的时候:
shared_ptr<int> ptr1 = make_shared<int>();
虽然只有1个调用通过make_shared
分配内存,但是有两个“逻辑”块不应该这样处理。 一个是存储实际值的int
,另一个是存储所有shared_ptr<>
“magic”的控制块。
它只是线程安全的控制块本身。
我把它放在自己的路线上来强调。 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<>
实例可以根据需要在不同的线程上创build和销毁。
//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<>
实例的线程安全性仅适用于pipe理相互初始化的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<>
是确保多个对象所有者确保对象被破坏的机制,而不是确保多个线程可以正确访问对象的机制。 你仍然需要一个单独的同步机制来安全地在multithreading中使用它(比如std :: mutex )。
最好的方式来考虑它IMO是shared_ptr<>
确保指向同一内存的多个副本本身没有同步问题,但对指向的对象不做任何事情。 像这样对待它。
为了增加Kevin所写的内容,C ++ 14规范支持对shared_ptr对象的primefaces访问:
20.8.2.6
shared_ptr
primefaces访问[util.smartptr.shared.atomic]如果仅通过本节中的函数完成访问,并且将实例作为第一个parameter passing,那么从多个线程并发访问
shared_ptr
对象不会引入数据争用。
所以如果你这样做:
//In thread 1 shared_ptr<myClass> private = atomic_load(&global); ... //In thread 2 atomic_store(&global, make_shared<myClass>()); ...
它将是线程安全的。
这意味着你将有一个有效的shared_ptr和有效的引用计数。
您正在描述尝试读取/分配给相同variables的2个线程之间的争用条件。
因为这是一般的未定义的行为(它只在个别程序的上下文和时间上有意义),shared_ptr不处理。
读操作本身不受数据竞争的影响,因此只要所有线程仅使用const方法 (包括创build它的副本),共享线程之间的shared_ptr的同一个实例是安全的。 只要一个线程使用非const方法(如在“指向另一个对象”中),这种使用就不再是线程安全的。
OP示例不是线程安全的,并且需要使用线程1中的primefaces负载以及线程2(C ++ 11中的2.7.2.5部分)中的primefaces存储以使线程安全。
MSDN文本中的关键词确实是不同的shared_ptr对象 ,如前面的答案中所述。