C++ std::共享线程安全

C++ std::共享线程安全,c++,std,shared-ptr,C++,Std,Shared Ptr,我读过 “多个线程可以同时读取和写入不同的数据 共享的\u ptr对象,即使这些对象是共享的副本 所有权。”() 这是否意味着更改共享\u ptr对象是安全的? 例如,下一个代码是否被认为是安全的: shared_ptr<myClass> global = make_shared<myClass>(); ... //In thread 1 shared_ptr<myClass> private = global; ... //In thread 2 glo

我读过

“多个线程可以同时读取和写入不同的数据 共享的\u ptr对象,即使这些对象是共享的副本 所有权。”()

这是否意味着更改共享\u ptr对象是安全的?
例如,下一个代码是否被认为是安全的:

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

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

//In thread 2
global = make_shared<myClass>();
...
shared_ptr global=make_shared();
...
//在线程1中
共享_ptr private=全局;
...
//在线程2中
全局=使_共享();
...
在这种情况下,我是否可以确定线程1
private
将具有
global
的原始值或线程2分配的新值,但无论哪种方式,它都将具有有效的myClass共享ptr

==编辑==
只是为了解释我的动机。我希望有一个共享指针来保存我的配置,并有一个线程池来处理请求。
因此,
global
是全局配置。
线程1
在开始处理请求时采用当前配置。
线程2
正在更新配置。(仅适用于未来的请求)


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

< P>这意味着您将拥有一个有效的<代码> SydDypTr>代码>和一个有效的引用计数。

您正在描述试图读取/分配给同一变量的两个线程之间的竞争条件


因为这通常是未定义的行为(它仅在单个程序的上下文和计时中有意义)
shared\u ptr
无法处理这一问题。

您所阅读的内容并不是您所认为的意思。首先,自己试试msdn页面

向下滚动到“备注”部分,您将看到问题的实质。基本上,
shared_ptr
指向一个“控制块”,它跟踪有多少
shared_ptr
对象实际指向“真实”对象。所以当你这样做的时候:

shared_ptr<int> ptr1 = make_shared<int>();
这很好,事实上,您可以在所有线程中任意执行此操作。然后当
local\u实例
被破坏时(通过超出范围),它也是线程安全的。有人可以访问
global\u实例
,这不会有什么区别。从msdn中提取的代码片段基本上意味着“对控制块的访问是线程安全的”,因此可以根据需要在不同的线程上创建和销毁其他
共享\u ptr
实例

//In thread 1
local_instance = make_shared<myClass>();
如果从任何其他线程(您说您正在这样做)访问
global\u实例
,这几乎肯定是不好的。如果您这样做是因为您要向
global\u实例所在的任何地方写入,而不仅仅是从中读取,那么它需要一个锁。因此,从多个线程写入对象是不好的,除非您通过锁保护它。因此,您可以通过从对象中指定新的
共享\u ptr
对象,从
全局\u实例
中读取对象,但不能对其进行写入

// 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 global_instance=make_shared(0);
空螺纹_fcn();
int main(int argc,字符**argv)
{
螺纹1(螺纹fcn);
螺纹2(螺纹fcn);
...
螺纹10(螺纹_fcn);
计时:毫秒持续时间(10000);
此线程::睡眠(持续时间);
返回;
}
无效线程_fcn()
{
//这是线程安全的,可以很好地工作,尽管它是无用的
//将创建并销毁短期指针。
对于(int i=0;i<10000;i++)
{
共享\u ptr temp=全局\u实例;
}
//这不是线程安全的。虽然所有线程都相同,但
//这一“最终”价值几乎肯定不会被低估
//线程数*10000=100000。这将是另外一回事。
对于(int i=0;i<10000;i++)
{
*全局_实例=*全局_实例+1;
}
}
shared_ptr
是一种确保多个对象所有者确保对象被销毁的机制,而不是一种确保多个线程可以正确访问对象的机制。您仍然需要一个单独的同步机制来在多个线程中安全地使用它(例如)


IMO最好的思考方法是
shared\u ptr
确保指向同一内存的多个副本本身没有同步问题,但不会对指向的对象执行任何操作。像这样对待它。

为了补充Kevin所写的内容,C++14规范对共享ptr对象本身的原子访问提供了额外的支持:

20.8.2.6
shared\u ptr
atomic访问[util.smartptr.shared.atomic]

如果通过本节中的函数以独占方式进行访问,并且实例作为其第一个参数传递,则从多个线程并发访问
共享\u ptr
对象不会引入数据竞争

因此,如果你这样做:

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

//In thread 2
atomic_store(&global, make_shared<myClass>());
...
//在线程1中
共享\u ptr private=原子\u加载(&global);
...
//在线程2中
原子存储(&global,make_shared());
...

它将是线程安全的。

读取操作之间不受数据竞争的影响,因此,只要所有线程仅使用const方法(包括创建它的副本),就可以在线程之间安全地共享共享相同的共享ptr实例。一旦一个线程使用非常量方法(如“将其指向另一个对象”),这种使用就不再是线程安全的

#include <iostream>
#include <memory>

using namespace std;

shared_ptr<int> sp1(new int(10));

int main()
{
    cout<<"Hello World! \n";

    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "---------\n";

    shared_ptr<int> sp2 = sp1;
    shared_ptr<int>* psp3 = new shared_ptr<int>;
    *psp3 = sp1;
    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "sp3 use count: " << psp3->use_count() << ", sp3: " << *(*psp3) << "\n";
    cout << "---------\n";

    sp1.reset(new int(20));

    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "sp3 use count: " << psp3->use_count() << ", sp3: " << *(*psp3) << "\n";
    cout << "---------\n";

    delete psp3;
    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "---------\n";

    sp1 = nullptr;

    cout << "sp1 use count: " << sp1.use_count() << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";

    return 0;
}
OP示例不是线程安全的,需要在线程1中使用原子负载。和线程2中的原子存储(C++11中的第2.7.2.5节),以使其线程安全

#include <iostream>
#include <memory>

using namespace std;

shared_ptr<int> sp1(new int(10));

int main()
{
    cout<<"Hello World! \n";

    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "---------\n";

    shared_ptr<int> sp2 = sp1;
    shared_ptr<int>* psp3 = new shared_ptr<int>;
    *psp3 = sp1;
    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "sp3 use count: " << psp3->use_count() << ", sp3: " << *(*psp3) << "\n";
    cout << "---------\n";

    sp1.reset(new int(20));

    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "sp3 use count: " << psp3->use_count() << ", sp3: " << *(*psp3) << "\n";
    cout << "---------\n";

    delete psp3;
    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "---------\n";

    sp1 = nullptr;

    cout << "sp1 use count: " << sp1.use_count() << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";

    return 0;
}
MSDN文本中的关键字实际上是不同的共享\u ptr对象,正如前面所述
// In thread n
shared_ptr<MyConfig> sp_local = sp_global;
// In thread 1
shared_ptr<MyConfig> sp_global = make_shared<MyConfig>(new MyConfig);
#include <iostream>
#include <memory>

using namespace std;

shared_ptr<int> sp1(new int(10));

int main()
{
    cout<<"Hello World! \n";

    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "---------\n";

    shared_ptr<int> sp2 = sp1;
    shared_ptr<int>* psp3 = new shared_ptr<int>;
    *psp3 = sp1;
    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "sp3 use count: " << psp3->use_count() << ", sp3: " << *(*psp3) << "\n";
    cout << "---------\n";

    sp1.reset(new int(20));

    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "sp3 use count: " << psp3->use_count() << ", sp3: " << *(*psp3) << "\n";
    cout << "---------\n";

    delete psp3;
    cout << "sp1 use count: " << sp1.use_count() << ", sp1: " << *sp1 << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";
    cout << "---------\n";

    sp1 = nullptr;

    cout << "sp1 use count: " << sp1.use_count() << "\n";
    cout << "sp2 use count: " << sp2.use_count() << ", sp2: " << *sp2 << "\n";

    return 0;
}
Hello World!
sp1 use count: 1, sp1: 10
---------
sp1 use count: 3, sp1: 10
sp2 use count: 3, sp2: 10
sp3 use count: 3, sp3: 10
---------
sp1 use count: 1, sp1: 20
sp2 use count: 2, sp2: 10
sp3 use count: 2, sp3: 10
---------
sp1 use count: 1, sp1: 20
sp2 use count: 1, sp2: 10
---------
sp1 use count: 0
sp2 use count: 1, sp2: 10
# Main Thread
shared_ptr<string> global_ptr = make_shared<string>();
string str = *global_ptr;

# Thread 1
global_ptr.reset();
# Main Thread
shared_ptr<string> global_ptr = make_shared<string>();
string str = *global_ptr;

# Thread 1
shared_ptr<string> local_ptr = global_ptr;
local_ptr.reset();
# Main Thread
shared_ptr<string> global_ptr = make_shared<string>();
string str = *global_ptr;

# Thread 1
shared_ptr<string> local_ptr = global_ptr;
(*local_ptr).clear();
std::shared_ptr<int> ptr = std::make_shared<int>(100);

for (auto i= 0; i<10; i++){
    std::thread([ptr]{                        
    auto local_p = ptr;  # read from ptr
    //...
    }).detach(); 
}
std::shared_ptr<int> ptr = std::make_shared<int>(100);

for (auto i= 0; i<10; i++){
    std::thread([&ptr]{                        
    ptr = std::make_shared<int>(200);
    //...           
    }).detach(); 
}
std::shared_ptr<int> ptr = std::make_shared<int>(100);
std::mutex mt;

for (auto i= 0; i<10; i++){
    std::thread([&ptr, &mt]{  
      std::scoped_lock lock(mt);                      
      ptr = std::make_shared<int>(200);
      //...                   
      }).detach(); 
}
std::shared_ptr<int> ptr = std::make_shared<int>(100);

for (auto i= 0; i<10; i++){
  std::thread([&ptr]{      
    std::atomic_store(&ptr, std::make_shared<int>(200));                   
  }).detach(); 
}