多读写器类的线程安全性

Thread safety of multiple-reader/single-writer class

本文关键字:线程 安全性 读写器      更新时间:2023-10-16

我正在处理一个经常读取但很少写入的集合。

class A {
  boost::shared_ptr<std::set<int> > _mySet;
public:
  void add(int v) {
    boost::shared_ptr<std::set<int> > tmpSet(new std::set<int>(*_mySet));
    tmpSet->insert(v);  // insert to tmpSet
    _mySet = tmpSet;    // swap _mySet
  }
  void check(int v) {
    boost::shared_ptr<std::set<int> > theSet = _mySet;
    if (theSet->find(v) != theSet->end()) {
      // do something irrelevant
    }
  }
};

在类中,add()只由一个线程调用,check()由多个线程调用。CCD_ 3不关心CCD_ 4是否是最新的。类线程安全吗?执行check()的线程是否可能观察到swap _mySet发生在insert to tmpSet之前?

这是shared_ptr实现线程安全的一个有趣用法。是否正常取决于boost::shared_ptr。特别是,它是否建立了某种fence或membar,这样就可以保证所有写入CCD_ 11的构造函数和CCD_指针值的修改变得可见。

我可以在Boost中找到没有线程安全保证智能指针的文档。这让我大吃一惊,因为我确信有一些。但快速查看1.47.0的来源,发现没有,并且在线程环境中使用CCD_ 12将失败(有人能告诉我我缺了什么吗?我不能认为boost::shared_ptr忽略了线程。)

无论如何,有三种可能性:不能使用共享指针在线程环境中(似乎就是这样),共享指针在线程环境中确保其自身的内部一致性,但是没有建立关于其他对象的排序,或者共享指针建立完全排序。只有在最后一种情况下你的代码是安全的。在第一种情况下,你需要某种形式的把所有东西都锁起来,第二天,你需要一些fence或membar,以确保实际完成必要的写入在发布新版本之前,他们将在正在尝试读取。

您确实需要同步,它不是线程安全的。一般来说,这并不重要,即使是像shared += value;这样简单的东西也不是线程安全的。

看看这里关于shared_ptr的线程安全的例子:Is boost shared_ptr<XXX>线程安全?

我也会质疑您在add()中的分配/交换以及在check() 中使用shared_ptr

更新:

我回去为shared_ptr重新安装了dox。。。在您的特定情况下,它很可能是线程安全的,因为shared_ptr的引用计数是线程安全。但是,由于不使用读/写锁,您正在进行(IMHO)不必要的复杂性操作。

最终这个代码应该是线程安全的:

atomic_store(&_my_set,tmpSet);

theSet = atomic_load(&_mySet);

(而不是简单的分配)

但我不知道shared_ptr的原子性支持的现状。

注意,以无锁的方式向shared_ptr添加原子性实际上是一件很难的事情;因此,即使实现了原子性,它也可能在互斥对象或用户模式自旋锁上中继,因此,有时可能会遇到性能问题

编辑:也许还应该添加_my_set成员变量的volatile限定符。。但我不确定它是否是原子操作

的语义所严格要求的