8

我相信我至少对 C++ 中的多线程基础知识有很好的了解,但是我从来没有能够在构造函数或析构函数中围绕共享资源锁定互斥锁得到明确的答案。我的印象是你应该锁定两个地方,但最近同事不同意。假设以下类被多个线程访问:

class TestClass
{
public:

   TestClass(const float input) :
      mMutex(),
      mValueOne(1),
      mValueTwo("Text")
   {
      //**Does the mutex need to be locked here?
      mValueTwo.Set(input);
      mValueOne = mValueTwo.Get();
   }

   ~TestClass() 
   { 
     //Lock Here?
   }

   int GetValueOne() const
   {
      Lock(mMutex);
      return mValueOne;
   }

   void SetValueOne(const int value)
   {
      Lock(mMutex);
      mValueOne = value;
   }

   CustomType GetValueTwo() const
   {
      Lock(mMutex);
      return mValueOne;
   }

   void SetValueTwo(const CustomType type)
   {
      Lock(mMutex);
      mValueTwo = type;
   }

private:

   Mutex mMutex;
   int mValueOne;
   CustomType mValueTwo;
};

当然,通过初始化列表,一切都应该是安全的,但是构造函数中的语句呢?在析构函数中,做一个非作用域的锁,从不解锁(本质上只是调用 pthread_mutex_destroy)会有好处吗?

4

2 回答 2

12

多个线程不能构造同一个对象,也不应允许任何线程在对象完全构造之前使用它。因此,在健全的代码中,没有锁定的构造是安全的。

破坏是一个稍微困难的情况。但同样,对对象进行适当的生命周期管理可以确保当某些线程可能仍使用它时,该对象永远不会被破坏。

共享指针可以帮助实现这一点,例如。:

  • 在某个线程中构造对象
  • 将共享指针传递给需要访问对象的每个线程(如果需要,包括构造它的线程)
  • 当所有线程释放共享指针时,对象将被销毁

但显然,存在其他有效的方法。关键是在对象生命周期的三个主要阶段之间保持适当的界限:构造、使用和销毁。绝不允许任何这些阶段之间发生重叠。

于 2012-07-25T15:13:23.173 回答
1

它们不必被锁定在构造函数中,因为外部任何人都可以访问该数据的唯一方法是从构造函数本身传递它们(或执行一些未定义的行为,例如调用虚拟方法) .

[编辑:删除了关于析构函数的部分,因为正如评论正确断言的那样,如果您尝试从可能已死的对象访问资源,您会遇到更大的问题]

于 2012-07-25T15:13:35.023 回答