的std :: shared_ptr的线程安全(std::shared_ptr thread saf

2019-07-18 14:17发布

我读过,

“多线程可以同时读取和写入不同的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?

== ==编辑
只是为了解释我的动机。 我想有一个共同的指向握住我的配置,我有一个线程池来处理请求。
所以global的全局配置。
thread 1走的是当前配置,因为它开始处理请求。
thread 2被更新配置。 (仅适用于未来的请求)

如果它的工作,我可以更新配置这种方式没有的请求处理中间打破它。

Answer 1:

你正在阅读是不是意味着你认为这意味着什么。 首先,尝试在MSDN页面shared_ptr的本身。

向下滚动到“备注”部分,你会得到这个问题的肉。 基本上,一个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拉段基本意思是“访问控制模块是线程安全的”,这样其他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;
    }
}

shared_ptr<>是一种机制,以确保多个对象所有者确保一个目的是破坏,不以确保多线程能够正确地访问对象的机制。 你还需要一个单独的同步机制,在多线程安全地使用它(如性病::互斥体 )。

国际海事组织去想它,最好的办法是, shared_ptr<>可以确保指向相同的存储多个副本没有同步问题本身 ,但不会做任何事情的指向的对象。 把它当作是这样的。



Answer 2:

要添加到什么凯文写道,在C ++ 14规范有原子访问shared_ptr的额外支持对象本身:

20.8.2.6 shared_ptr原子访问[util.smartptr.shared.atomic]

到并发访问shared_ptr如果访问是专门通过功能在本节进行,该实例作为他们的第一个参数传递从多个线程对象不引入数据争。

所以,如果你这样做:

//In thread 1
shared_ptr<myClass> private = atomic_load(&global);
...

//In thread 2
atomic_store(&global, make_shared<myClass>());
...

这将是线程安全的。



Answer 3:

这意味着你将有一个有效shared_ptr ,和一个有效的引用计数。

你所描述的2个线程之间的竞争条件正试图读/分配给同一个变量。

因为这是一般未定义的行为(它才有意义,在个别项目的背景和时机) shared_ptr不处理。



Answer 4:

读操作不受它们之间的数据竞争,因此它是安全的,只要所有线程使用const的方法线程之间共享shared_ptr的的同一个实例(这包括创建它的副本)。 只要一个线程使用非const方法(如“它指向另一个对象”)这样的使用不再是线程安全的。

的OP示例不是线程安全,并需要(在C ++ 11节2.7.2.5)在线程1和线程在2原子商店使用原子负荷,使其线程安全的。

在MSDN文本的关键词是确实不同shared_ptr对象 ,如以前的答案已经提到。



文章来源: std::shared_ptr thread safety