简体   繁体   English

std::atomic_... 应该怎么做?<std::shared_ptr> 用于线程安全类的复制和移动操作?

[英]How should std::atomic_...<std::shared_ptr> be used in the copy and move operations of a thread safe class?

I have the following class that is supposed to be thread safe and has a std::shared_ptr member that refers to some shared resource.我有以下类,它应该是线程安全的,并且有一个std::shared_ptr成员,它引用了一些共享资源。

class ResourceHandle
{
    using Resource = /* unspecified */;
    std::shared_ptr<Resource> m_resource;
};

Multiple threads may acquire a copy of a resource handle from some central location and the central resource handle may be updated at any time.多个线程可以从某个中央位置获取资源句柄的副本,并且可以随时更新中央资源句柄。 So reads and writes to the same ResourceHandle may take place concurrently.因此对同一个 ResourceHandle 的读取和写入可能同时发生。

// Centrally:
ResourceHandle rh;

// Thread 1: reads the central handle into a local copy for further processing
auto localRh = rh;

// Thread 2: creates a new resource and updates the central handle
rh = ResourceHandle{/* ... */};

Because these threads perform non-const operations on the same std::shared_ptr , according to CppReference , I should use the std::atomic_...<std::shared_ptr> specializations to manipulate the shared pointer.因为这些线程在同一个std::shared_ptr上执行非常量操作,根据CppReference ,我应该使用std::atomic_...<std::shared_ptr>来操作共享指针。

If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;如果多个执行线程在没有同步的情况下访问同一个 shared_ptr 并且这些访问中的任何一个使用了 shared_ptr 的非常量成员函数,那么将发生数据竞争; the shared_ptr overloads of atomic functions can be used to prevent the data race.原子函数的 shared_ptr 重载可用于防止数据竞争。

So I want to implement the copy and move operations of the ResourceHandle class using these atomic operations such that manipulating a single resource handle from multiple threads avoids all data races.所以我想使用这些原子操作来实现ResourceHandle类的复制和移动操作,以便从多个线程操作单个资源句柄避免所有数据竞争。

The notes section of the CppReference page on std::atomic_...<std::shared_ptr> specializations states the following:关于std::atomic_...<std::shared_ptr>专业化的CppReference 页面的注释部分声明如下:

To avoid data races, once a shared pointer is passed to any of these functions, it cannot be accessed non-atomically.为避免数据竞争,一旦将共享指针传递给这些函数中的任何一个,就不能以非原子方式访问它。 In particular, you cannot dereference such a shared_ptr without first atomically loading it into another shared_ptr object, and then dereferencing through the second object.特别是,如果不先将此类 shared_ptr 原子加载到另一个 shared_ptr 对象中,然后通过第二个对象取消引用,则无法取消引用它。

So I probably want to use some combination of std::atomic_load and std::atomic_store , but I am unsure where and when they should be applied.所以我可能想使用std::atomic_loadstd::atomic_store某种组合,但我不确定应该在何时何地应用它们。

How should the copy and move operations of my ResourceHandle class be implemented to not introduce any data races?我的ResourceHandle类的复制和移动操作应该如何实现才能不引入任何数据竞争?

std::shared_ptr synchronises it's access to the reference count, so you don't have to worry about operations on one std::shared_ptr affecting another. std::shared_ptr同步它对引用计数的访问,因此您不必担心对一个std::shared_ptr影响另一个。 If those are followed by at least one modification to the pointee, you have a data race there .如果这些之后至少对指针进行了一次修改,那么您就会在那里进行数据竞争。 Code that shares ownership of a previous Resource will be unaffected by m_resource being reset to point to a new Resource .共享先前Resource所有权的代码将不受m_resource被重置为指向新Resource

You have to synchronise access to a single std::shared_ptr , if that is accessible in multiple threads.如果可以在多个线程中访问,则必须同步对单个std::shared_ptr访问。 The warning provided (and the reason it is deprecated in C++20) states that if anywhere is atomically accessing a value, everywhere that accesses that value should be atomic.提供的警告(并且在C ++ 20弃用的原因)指出,如果在任何地方以原子到处访问的值,即访问该值应是原子的。

You could achieve that by hiding the global std::shared_ptr behind a local copies.您可以通过将全局std::shared_ptr隐藏在本地副本后面来实现这一点。 ResourceHandle as a separate class makes that more difficult. ResourceHandle作为一个单独的类使这变得更加困难。

using ResourceHandle = std::shared_ptr<Resource>;
static ResourceHandle global;

ResourceHandle getResource()
{
    return std::atomic_load(&global);
}

void setResource(ResourceHandle handle)
{
    std::atomic_store(&global, handle);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM