互斥锁与对象

4

我想了解如何在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。然而,如果取消注释互斥锁,我会得到一堆关于“使用已删除函数”的编译器错误,这些函数包括赋值运算符和复制运算符。我在安全锁定对象方面错过了什么?
2个回答

7

使一个类成为线程安全的简单方法是添加一个互斥量属性,并在访问器方法中锁定互斥量。

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;
  }
};

然而,这意味着当任何一个实例被访问时,该类的每个实例都会阻塞,因为它们共享相同的互斥锁。
理论上,包含非静态互斥锁的类可以通过手动编写复制构造函数和赋值运算符来使其可复制,从而省略互斥锁。然而,这样做很困难且繁琐,特别是对于具有大量属性并在开发过程中经常更改的类。
如果不接受阻止访问所有实例的静态互斥锁,则最好且最简单的方法是在类外部维护互斥锁。可能会感到不幸,以这种方式暴露类的内部工作方式,但是其他选择更加复杂,因此不可靠,而当互斥锁在访问类的代码级别处理时,我经常发现重要的优化。

除非我误解了他的意思,否则他需要在所有线程中使用相同的互斥锁。将其放入容器中使用移动语义不会起作用。(另一方面,目前每个线程都有自己的Rope对象,因此不需要互斥锁。当问题没有清楚地解释时,很难说出正确的解决方案。) - James Kanze
@Mike Seymour:std::mutex不可移动。可以通过在每个Rope构造函数中默认构造std::mutex,使Rope可移动或可复制。或者像James建议的那样,将互斥锁设置为静态变量。哪种方法更适合取决于只有OP在此时才知道。 - Howard Hinnant
我的错误;我在发布前应该检查我的假设。 - Mike Seymour

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接