1

在 MFC 应用程序中,我有 2 个线程访问我的数据,因此我使用 CCriticalSection Lock 和 Unlock 来保护我的数据不会同时被 2 个线程访问。

从其中一个线程我需要锁定我的数据两次,

线程 1,

  void ProcessFunction()
  {
         LockData();                  // LockData and unLockData internally uses CCriticalSection
         if( IsDataAvailable() )
         {

         }
         UnLockData();
  }

IsDataAvialable 已经有 LockData()

   IsDataAvailable()
   {
     LockData();
     bool bAvailable = .....;
     UnLockData();
     return bAvialble;
   }

线程 2

     void Delete() 
     {
        LockData();

        ....
        UnLockData();
     }

当我从线程 1 测试第二个锁时,没有等待第一个锁调用解锁,

但是一个线程的一个锁正在等待另一个线程调用解锁。

我知道一个线程的锁将等待另一个线程的锁计数变为0。但如果被同一个线程锁定,它不会等待同一个线程解锁。这是预期的行为。

我想知道在同一个线程中多次调用 Lock 是否是好习惯?

4

3 回答 3

4

您可能在这里寻找的艺术术语是“递归互斥锁”或“可重入互斥锁”。问题是,CCriticalSection 是否支持递归锁定(已经锁定在同一个线程中时锁定)?据我所知(例如http://microsoft.public.vc.mfc.narkive.com/gjxzQaHf/ccriticalsection),确实如此。

这是一个关于递归互斥锁的有趣讨论,它说你不应该使用它们:http ://www.zaval.org/resources/library/butenhof1.html

于 2013-10-17T13:00:24.640 回答
2

这称为“递归锁”。

有些人不喜欢他们。您编写了一个函数,该函数IsDataAvailable()有时在持有锁时调用,有时在未持有时调用。在这种情况下,它不需要知道是哪个,因为它在它所做的所有事情上都使用了锁,但是编写这样的函数仍然有些危险。执行锁定的代码应该知道已经持有哪些锁才能正确使用锁。示例包括避免锁定反转,并确保如果您需要释放锁(例如在使用条件变量时),您可以这样做而不会给认为锁一直持有的调用者带来问题。

你总是可以满足那些不喜欢递归锁的人,通过编写两个版本的IsDataAvailable(): 一个在不加锁的情况下计算值,另一个获取锁,调用第一个,然后释放锁。然后在任何给定的上下文中调用“正确的”。

不过,这最终可能会创建很多变体函数。如果您正确使用递归锁,它们并没有什么问题,因此您必须判断额外的工作是否会帮助您始终正确使用锁。

于 2013-10-17T13:04:08.663 回答
1

使用递归锁定有用例,但这不是其中之一......所以不,这不是一个好习惯。99% 的时间线程安全不应对象(或概念对象,可能是您的情况)内完成,以便该对象可以在对象外部的不同线程中使用。

这是关注点分离和对象的通用使用的问题。data有时可能需要使用单线程,在这种情况下,您不希望锁定所有这些操作。即使不是这种情况,从概念上讲数据属于 in data,如果您想安全地data跨多个线程使用,您应该在其他地方处理。

将这些概念分开会带来很大的灵活性:您可以使用不同类型的锁定,例如shared_mutex等。您可以使用相同的锁定来操作多个对象。

这似乎会因为到处都锁定而弄脏您的代码。这有点真实。您可以使用一些模式来帮助...例如monitor

于 2013-10-17T13:40:20.567 回答