c++ - 这是带有 shared_ptr 的正确 C++11 双重检查锁定版本吗?

标签 c++ multithreading c++11

article Jeff Preshing 指出双重检查锁定模式 (DCLP) 在 C++11 中得到修复。用于此模式的经典示例是单例模式,但我碰巧有一个不同的用例,而且我仍然缺乏处理“原子 <> 武器”的经验 - 也许这里有人可以帮助我。

Jeff 在 "Using C++11 Sequentially Consistent Atomics" 下描述的以下代码是否是正确的 DCLP 实现? ?

class Foo {
    std::shared_ptr<B> data;
    std::mutex mutex;

    void detach()
    {
      if (data.use_count() > 1)
      {
        std::lock_guard<std::mutex> lock{mutex};
        if (data.use_count() > 1)
        {
          data = std::make_shared<B>(*data);
        }
      }
    }

public:
  // public interface
};

最佳答案

不,这不是 DCLP 的正确实现。

事情是你的外部检查 data.use_count() > 1 访问对象(类型为B with reference count),可以在互斥保护部分删除(取消引用)。任何类型的内存栅栏都无济于事。

为什么 data.use_count() 访问对象:

假设这些操作已经执行:

shared_ptr<B> data1 = make_shared<B>(...);
shared_ptr<B> data = data1;

那么你有以下布局(此处未显示对weak_ptr 的支持):

         data1             [allocated with B::new()]         data
                           --------------------------
 [pointer type] ref; -->   |atomic<int> m_use_count;|    <-- [pointer type] ref
                           |B obj;                  |
                           --------------------------

每个shared_ptr 对象只是一个指针,指向已分配的内存区域。该内存区域嵌入了 B 类型的对象加上原子计数器,反射(reflect)了指向给定对象的 shared_ptr 的数量。当这个计数器变为零时,内存区域被释放(B 对象被销毁)。 shared_ptr::use_count() 返回的正是这个计数器。

UPDATE:执行,这会导致访问已经释放的内存(最初,两个 shared_ptr 指向同一个对象,.use_count() 为 2):

/* Thread 1 */                   /* Thread 2 */       /* Thread 3 */
Enter detach()                   Enter detach()
Found `data.use_count()` > 1     
Enter critical section                                   
Found `data.use_count()` > 1
                                 Dereference `data`,
                                 found old object.
Unreference old `data`,
`use_count` becomes 1 
                                                      Delete other shared_ptr,
                                                      old object is deleted
Assign new object to `data`
                                 Access old object
                                 (for check `use_count`)
                                 !! But object is freed !!

外部检查只需要一个指向对象的指针来决定是否需要获取锁。

顺便说一句,即使你的实现也是正确的,它有一点意义:

  1. 如果可以同时从多个线程访问data(和detach),对象的唯一性没有优势,因为它可以从多个线程访问。如果你想改变对象,所有对 data 的访问都应该被外部互斥锁保护,在这种情况下 detach() 不能并发执行。

  2. 如果data(和detach)只能同时被单线程访问,detach实现不会根本不需要任何锁定。

关于c++ - 这是带有 shared_ptr 的正确 C++11 双重检查锁定版本吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30911849/

相关文章:

C++返回技巧

c++ - 具有模板功能的跨平台类

c++ - QPrintDialog 在窗口中显示两次

c# - 取消线程并重新启动它

c++ - TCP 接收使用率 100% CPU

c++ - 使用可变参数模板在 C++ 中包装函数指针

c++ - 带有模板函数的 clang 编译错误

c++ - 正在尝试索引数字值。 C++ 和路亚

ruby - Ruby 的 Net::HTTP 在 JRuby 上是线程安全的吗

c++ - 计算一个单词中的音节数,但考虑到单词中任何相邻的元音都算作一个音节