【问题标题】:C++ thread-safe bracket operator proxyC++ 线程安全括号运算符代理
【发布时间】:2016-04-08 23:11:55
【问题描述】:

给定一个标准向量的简单包装器,以线程安全的方式实现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;
};

这已经足够了吗?还是我错过了什么会炸掉我的腿?

【问题讨论】:

  • 您目前禁止 (example[i] = 4.2) = 42; 作为您的 operator = 返回 double 而不是 bracket_operator_proxy&amp;。不是陷阱。
  • @Jarod42:是的,这就是意图。否则,我认为,我必须解锁析构函数中的互斥锁。有什么更好的选择吗?
  • 互斥锁在operator=末尾解锁,所以返回bracket_operator_proxy&amp;应该没问题。 BTW std::vector&lt;std::atomic&lt;double&gt;&gt; 似乎是一个不错的选择。
  • 我在您的代码中看不到任何缺陷
  • 和@Jarod42 一样,我也会使用std::vector<:atomic>>。在不影响相同矢量元素的情况下允许并发工作,更少的编码(和更少的出错机会)。

标签: c++ multithreading concurrency


【解决方案1】:

拥有operator-&gt;(非常有用)后,您需要返回一个-&gt; 代理,该代理将锁的生命周期延长到语句结束,并使您面临单线程死锁。

查看线程安全的 monads/functor/wrapper,例如 the one here。它不会使锁完全透明,但它们不应该是

  • 不要在线程之间共享数据

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

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

  • 如果做不到,请考虑重新设计

  • 真的。可能是原子的?

  • 有一组有限的函数来明确管理锁

  • 好的,现在像上面一样包装在 reader/writer monad 中,使用简单的复合操作

  • 编写神奇地获得锁的代码,看起来就像非线程交互代码,从而使您的读者误以为安全和效率

偏好递减。

线程安全的危险和困难的部分不是语法笨拙的事实。正是基于锁的线程安全几乎不可能被证明是正确和安全的。使语法更易于使用并不是一个高价值的目标。

例如,v[i]=v[i+1] 缺乏同步:在读取和写入之间,任何事情都可能发生变化。更别说“i是一个有效的索引吗?”

【讨论】:

  • 感谢您的回答、线程安全包装器的链接以及可能出错的示例。不知何故,在我看来,多线程必须是丑陋的......
  • 鉴于我提供了使用指针而不是引用并提供了合理的copy constructor 和类赋值运算符,它是否成为线程安全的? (我知道你说几乎不可能证明,所以请跟随你的直觉)。问题是我已经在我的库中的不同位置使用了operator[],并进一步鼓励用户扩展功能——然后imo应该尽可能简单。
  • @davidhigh 单线程代码和多线程代码,在多线程代码中,您在不同位置获取和释放锁,而其他线程修改内容,是根本不同的野兽。像a[i] = a[i-1]+1 这样简单,在多线程代码中与单线程代码的含义完全不同(单线程后置条件是a[i]a[i-1] 大一个,但那不是真的在多线程代码中)。编译现有代码是编写多线程代码的一种可怕方式。如果您有超过 1 个锁或不可重入锁,它几乎总是会被锁定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
  • 2010-10-22
相关资源
最近更新 更多