std :: shared_ptr线程安全 [英] std::shared_ptr thread safety

查看:336
本文介绍了std :: shared_ptr线程安全的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已读过


多线程可以同时读取和写入不同的
shared_ptr对象,即使对象是共享
所有权的副本。 ( MSDN:标准C ++库中的线程安全


这是否意味着改变shared_ptr对象是安全的?

对于一个实例,

  shared_ptr< myClass> global = make_shared< myClass>(); 
...

//在线程1
shared_ptr< myClass> private = global;
...

//在线程2
global = make_shared< myClass>();
...

我可以肯定在那种情况下,线程1 private 将具有原始值全局或线程2分配的新值,但无论哪种方式,它将有一个有效的shared_ptr到myClass?



== EDIT ==

只是为了解释我的动机。我想有一个共享指针来保存我的配置,我有一个线程池来处理请求。

所以全局是全局配置。 >
线程1 在开始处理请求时采用当前配置。

线程2 正更新配置。 (仅适用于未来的请求)



如果是工作,我可以更新配置,而不会在请求处理过程中中断。

解决方案

你读的内容并不意味着你的想法。首先,尝试 shared_ptr 本身的msdn页面。



向下滚动到备注部分,你会得到问题的肉。基本上, shared_ptr 指向一个控制块,它是如何跟踪多少 shared_ptr 对象指向真实对象。所以当你这样做:

  shared_ptr< int> ptr1 = make_shared< int>(); 

然后真正发生的是两个不同的内存段被分配。它一次完成,但它是两个逻辑块。一个是存储实际值的 int ,另一个是控制块,它存储所有 shared_ptr


我把它放在自己的行上以强调。 shared_ptr 内容不是线程安全的,也不是写入相同的 shared_ptr instance。这里有一些东西来演示我的意思:

  //在main()
shared_ptr< myClass> global = make_shared< myClass>();

//在线程1
shared_ptr< myClass> private = global;

这很好,事实上你可以根据需要在所有线程中做到这一点。然后当私有被破坏时,它也是线程安全的。有人可以访问全局,它不会有所作为。你从msdn提取的代码段基本上意味着访问控制块是线程安全的,所以其他 shared_ptr 实例可以根据需要在不同的线程上创建和销毁。 / p>

  //在线程1 
private = make_shared< myClass>();

这很好。它将影响全局对象,但仅间接。它的控制块将递减,但是以线程安全的方式完成。 private 将不再指向与全局相同的对象。

  //在线程2中
global = make_shared< myClass>();

这几乎肯定不是很好,如果全局是从任何其他线程访问做)。

  //在主题3中
* global = 3;
int a = * global;

//在线程4
* global = 7;

a 的值未定义。它可能是7,或者它可能是3,或者它可能是任何其他。 shared_ptr 实例的线程安全性仅适用于管理相互初始化的 shared_ptr 实例,而不是它们



为了强调我的意思,看看:

  shared_ptr< int> global_ptr = 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_ptr;
}

//这不是线程安全的。虽然所有的线程是一样的,
//final的值几乎肯定不会是
// number_of_threads * 10000 = 100,000。这将是别的东西。
for(int i = 0; i <10000; i ++)
{
* global = * global + 1;
}
}

A shared_ptr 是确保多个对象所有者确保对象被破坏的机制,而不是确保多个线程能够正确访问对象的机制。您仍然需要单独的同步机制,以在多个线程中安全地使用它(例如 std :: mutex )。



考虑它的最佳方式IMO是 shared_ptr 确保指向同一内存的多个副本对于本身有同步问题,但不会对指向的对象执行任何操作。像这样对待。


I've read that

"Multiple threads can simultaneously read and write different shared_ptr objects, even when the objects are copies that share ownership." (MSDN: Thread Safety in the Standard C++ Library)

Does that mean that changing shared_ptr object is safe ?
For an instance, is the next code considered safe:

shared_ptr<myClass> global = make_shared<myClass>();
...

//In thread 1
shared_ptr<myClass> private = global;
...

//In thread 2
global = make_shared<myClass>();
...

Can I be sure in that case that thread 1 private will have the original value of global or the new value which thread 2 assigned but either way it will have a valid shared_ptr to myClass?

==EDIT==
Just to explain my motivation. I want to have a shared pointer to hold my configuration and I have a thread pool to handle requests.
so global is the global configuration.
thread 1 is taking the current configuration as it start to handle a request.
thread 2 is updating the configuration. (only apply to future requests)

If it's work, I can update the configuration that way without breaking it in the middle of a request handling.

解决方案

What you're reading isn't meaning what you think it means. First of all, try the msdn page for shared_ptr itself.

Scroll down into the "Remarks" section and you'll get to the meat of the issue. Basically, a shared_ptr points to a "control block" which is how it keeps track of how many shared_ptr objects are actually pointing to the "Real" object. So when you do this:

shared_ptr<int> ptr1 = make_shared<int>();

Then what's really happening is TWO different sections of memory are being allocated. It's done at one time, but it's two "logical" blocks. One is the int which stores the actual value, and the other is the control block, which stores all the shared_ptr "magic" that makes it work.

It is only the control block itself which is thread-safe.

I put that on its own line for emphasis. The contents of the shared_ptr are not thread-safe, nor is writing to the same shared_ptr instance. Here's something to demonstrate what I mean:

// In main()
shared_ptr<myClass> global = make_shared<myClass>();

//In thread 1
shared_ptr<myClass> private = global;

This is fine, in fact you can do this in all threads as much as you want. And then when "private" is destructed, it is also thread-safe. Somebody can be accessing global and it won't make a difference. The snippet you pulled from msdn basically means "access to the control block is thread-safe" so other shared_ptr instances can be created and destroyed on different threads as much as necessary.

//In thread 1
private = make_shared<myClass>();

This is fine. It will affect the global object, but only indirectly. The control block for it will be decremented, but done in a thread-safe way. private will no longer point to the same object as global does.

//In thread 2
global = make_shared<myClass>();

This is almost certainly not fine if global is accessed from any other threads (which you say you're doing). It needs a lock if you're doing this.

// In thread 3
*global = 3;
int a = *global;

// In thread 4
*global = 7;

The value of a is undefined. It might be 7, or it might be 3, or it might be anything else as well. The thread-safety of the shared_ptr instances only applies to managing shared_ptr instances which were initialized from each other, not what they're pointing to.

To emphasize what I mean, look at this:

shared_ptr<int> global_ptr = 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_ptr;
    }

    // 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 = *global + 1;
    }
}

A shared_ptr is a mechanism to ensure that multiple object owners ensure an object is destructed, not a mechanism to ensure multiple threads can access an object correctly. You still need a separate synchronization mechanism to use it safely in multiple threads (like std::mutex).

The best way to think about it IMO is that shared_ptr makes sure that multiple copies pointing to the same memory don't have synchronization issues for itself, but doesn't do anything for the object pointed to. Treat it like that.

这篇关于std :: shared_ptr线程安全的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆