与对象的互斥

mutexes with objects

本文关键字:对象      更新时间:2023-10-16

我正在努力了解如何在c++中对对象使用互斥。我使用以下(琐碎的)多线程代码作为速度测试:

 struct Rope{
   int n, steps, offset;
   //std::mutex mut;
   Rope() {}
   Rope(int n, int steps, int offset) : n(n), steps(steps), offset(offset) {}
   void compute(){
     double a[n];
     for (int i=0; i<n; i++)
       a[i] = i + offset;
     for (int step=0; step<steps; step++)
       for (int i=0; i<n; i++)
     a[i] = sin(a[i]);
   }
 };
 void runTest(){
   int numRuns = 30;
   int n = 10000;
   int steps = 10000;
   std::vector<Rope> ropes;
   std::vector<std::thread> threads;
   for (int i=0; i<numRuns; i++)
     ropes.push_back(Rope(n, steps, i));
   for (auto& r : ropes)
     threads.push_back(std::thread(&Rope::compute, r));
   for (std::thread& t : threads)
     t.join();
 }    

代码运行得很好,在我的4核机器上速度提高了约4倍。当然,我没有在Rope中存储任何内容,所以不需要互斥。如果我现在认为我确实有一些需要保护的数据,我想在Rope上附加一个互斥对象,并(例如)在compute()循环中调用std::lock_guard。然而,如果我取消对互斥对象的注释,我会得到一堆关于赋值和复制运算符"使用已删除函数"的编译器错误。在安全锁定对象的目标中,我缺少什么?

使类线程安全的直接方法是添加互斥属性并将互斥锁定在访问器方法中

class cMyClass {
  boost::mutex myMutex;
  cSomeClass A;
public:
  cSomeClass getA() {
    boost::mutex::scoped_lock lock( myMutex );
    return A;
  }
};

问题是,这使得类不可复制。这一点很重要,尤其是当您想将类的对象存储在容器中时。

我可以通过将互斥对象设为静态对象来使事情正常工作。

class cMyClass {
  static boost::mutex myMutex;
  cSomeClass A;
public:
  cSomeClass getA() {
    boost::mutex::scoped_lock lock( myMutex );
    return A;
  }
};

然而,这意味着当访问任何其他实例时,类的每个实例都会阻塞,因为它们都共享相同的互斥对象。

从理论上讲,一个包含非静态互斥的类可以通过手工编码复制构造函数和赋值运算符来实现可复制性,从而省去互斥。然而,这很难做到,也很乏味,尤其是对于一个具有大量属性的类,这些属性在开发过程中经常发生变化。

如果一个静态互斥体在被阻止时阻止对类的所有实例的访问是不可接受的,那么最好和最简单的方法就是在类之外维护互斥体。以这种方式公开类的内部工作似乎很不幸,但替代方案要复杂得多,因此不可靠,而且当在访问类的代码级别处理互斥对象时,我经常发现有重要的优化。

您忽略了mutex不可复制这一事实。这意味着什么复制互斥对象?获取和释放互斥的位置处于Rope::compute中,并且由于所有线程都必须访问相同的互斥,您必须在runTest中定义它,并通过引用或通过指针。