C++线程安全括号运算符代理

C++ thread-safe bracket operator proxy

本文关键字:运算符 代理 线程 安全 C++      更新时间:2023-10-16

给定一个围绕标准向量的简单包装器,以线程安全的方式实现operator[]以便能够像往常一样设置内容的好方法是什么?

struct bracket_operator_proxy;
struct example
{
    auto operator[](size_t i) const { return bracket_operator_proxy(v, m, i); }
private:
    std::vector<double> v;
    std::mutex m;
};

以下是我对bracket_operator_proxy:的快速而天真的尝试

struct bracket_operator_proxy
{
     bracket_operator_proxy(std::vector<double>& v, std::mutex& m, int i)
        : v(v), m(m), i(i) {}
     operator double() const
     {
         std::lock_guard<std::mutex> l(m);
         return v[i];
     }
     auto operator=(double d)
     {
         std::lock_guard<std::mutex> l(m);
         v[i] = d;
         return d;
     }
     //... further assignment operators ...
private:
     std::vector<double>& v;
     std::mutex& m;
     int i;
};

这已经足够了吗?还是我错过了什么会把我的腿炸飞的东西?

一旦您有了operator->(这非常有用),您将需要返回一个->代理,它将延长锁的生存期,直到语句结束,并使您面临单线程死锁。

看看像这里这样的线程安全monads/functor/包装器。它不会使锁完全透明,但它们不应该是

  • 不在线程之间共享数据

  • 如果您共享数据,请使其成为不可变的

  • 如果必须进行变异,则通过已知安全设计的瓶颈隔离访问。一个消息队列说。

  • 如果你不能做到这一点,可以考虑重新设计

  • 真的。也许是原子弹?

  • 具有一组有限的功能,可以显式管理锁定

  • 好的,现在用简单的ish复合操作完成上面的读写器monad

  • 让代码神奇地获得锁,看起来就像非线程交互的代码,从而让读者产生一种虚假的安全感和效率

偏好递减。

线程安全的危险和困难之处并不在于语法的尴尬。基于锁的线程安全性几乎不可能被证明是正确和安全的。使语法更易于使用并不是一个高价值的目标。

例如,v[i]=v[i+1]充满了缺乏同步的问题:在读取和写入之间,任何事情都可能发生变化。更不用说"i是有效索引吗?"

的问题了