【问题标题】:Is there any way that Enter/LeaveCriticalSection could leave a handle behind有什么方法可以让 Enter/LeaveCriticalSection 留下句柄
【发布时间】:2009-11-19 13:32:12
【问题描述】:

我的程序中有以下代码:

  EnterCriticalSection(&critsec[x]);
  // stuff
  LeaveCriticalSection(&critsec[x]);

它在 99.999% 的情况下都能正常工作,但有时似乎会留下一个句柄。现在我已经做了一些显而易见的事情,比如确保 x 在输入之间没有改变值,并确保“// stuff”中没有任何“return”或“break”,但我想知道是否可能有什么东西否则会导致进入/离开对留下句柄。可能是内存不足或操作系统中的某些计数器溢出或其他原因。

编辑:我是 C++ 的新手,该程序最近才从 C 转换过来。整个程序的任何地方都没有例外。

【问题讨论】:

标签: c++ multithreading windows-xp


【解决方案1】:

如果您没有明确删除临界区,并且如果临界区曾经发生争用,您将泄漏句柄。当两个或多个线程在尝试进入单个临界区时重叠时,Windows 上临界区的某些实现会分配一个信号量。

这不是泄漏。或者更确切地说,如果“泄漏”句柄的数量小于或等于您正在使用的全局关键部分的数量,则不是泄漏。

【讨论】:

  • 这听起来很像答案! (除了我不确定我是否理解它!)你是说当我拥有并使用一组关键部分时,只有在存在争用时才会创建句柄?
  • 我很困惑——我当然不想删除关键部分。我只想删除由争用创建的句柄。我会想一遍又一遍地使用关键部分。
  • 刚刚在其他地方确认您的答案是正确的。没有错误,没有泄漏。由于争用很少,我只是被缓慢但稳定的句柄增长吓倒了。
【解决方案2】:

米克,

可能会发生很多事情。

异常可能导致控制流在执行 LeaveCriticalSection 调用之前退出块。为避免此问题,您可以使用资源获取即初始化 (RAII) 模式将临界区的进入和退出封装在基于堆栈的对象中。

但是,如果没有更完整的清单,就不可能说您的代码是否还有其他问题。

干杯 赛博

【讨论】:

  • 这很尴尬,但我不知道什么是“异常”。顺便说一句,我是一名长期的 C 程序员并且对 C++ 相当陌生,异常是 C++ 的概念吗?
  • @Mick:您可以在这里快速了解一下:parashift.com/c++-faq-lite/exceptions.html
  • 如果我理解正确 - 我的程序中不会发生异常,除非我把它们放在那里。我确定我没有。
  • 如果您没有从代码中抛出异常,那么它可能是在发生非常糟糕的事情时抛出的结构化异常之一,例如 AccessViolation 或 StackOverflow 等。
  • 所以您的意思是 Enter/LeaveCriticalSection 本身可能已经失败。有什么方法可以检测到这个吗?我注意到两者都没有返回任何值。
【解决方案3】:

最可能的原因是异常。您是否在此函数中捕获异常以及它们是否调用 Leave?另外,请注意,最好使用CSingleLock 类来锁定临界区,而不是像这样使用原始 API。通过使用 CSingleLock,您可以确保在出现异常时进行适当的清理。

【讨论】:

    【解决方案4】:

    由于您处于 C++ 领域,因此留下 // stuff 部分的异常将跳过 LeaveCriticalSection()。查找 RAII(“Resource-Aquisition-Is-Initialization”)作为防止这种情况发生的工具。下面是这样一个类的一个简单的例子:

    class CriticalSectionLock {
    public:
      CriticalSectionLock(CRITICAL_SECTION& c) : cs_(c){EnterCriticalSection(&cs_);}
      ~CriticalSectionLock()                           {LeaveCriticalSection(&cs_);}
    private:
      CRITICAL_SECTION& cs_;
    };
    
    
    void f()
    {
      CriticalSectionLock lock(critsec[x])
       // stuff
    } // lock's destructor will automagically call LeaveCriticalSection()
    

    附带说明:死锁有时会给人以某些锁未正确解锁的印象。

    【讨论】:

      【解决方案5】:

      扩展 sbi 的答案(正如您说您是 C++ 新手),异常会忽略从调用它的位置开始的其余代码,直到它到达可以处理异常的位置(“捕获” ) - 唯一的例外是当异常将内存包装在堆栈中时 - 它调用堆栈变量的析构函数。

      为确保始终调用“离开”,请使用以下类:(请原谅缺少格式),并将此类的实例和关键代码都放入新堆栈中。这可确保在非异常和异常情况下始终调用“离开”。

      编辑:更新 p-o-c 代码以反映评论。

      class AutoCritical
      {
      public:
        AutoCritical(CritSec * p_CritSec) : m_Sec(p_CritSec) 
         { EnterCriticalSection(m_CritSec); };
        ~AutoCritical() { LeaveCriticalSection(m_CritSec); };
      private:
        CritSec * m_Sec;
      }; 
      

      呼叫地点:

      // non-critical code ....
      {   //open stack for critical code
          AutoCritical a(&critsec[x]);
          // do critical stuff here ...
      }   // close stack
      

      【讨论】:

        【解决方案6】:

        您是否可能不会因为不匹配的 Enter/Leave 而丢失句柄,而是忘记调用 DeleteCriticalSection。

        【讨论】:

        • 我所有的关键部分都是全局变量。我在 main 开始时初始化它们,从那时起关键部分的数量保持不变。我从不删除它们——我只是让操作系统在我退出程序时杀死它们。
        猜你喜欢
        • 1970-01-01
        • 2011-08-18
        • 2020-06-02
        • 1970-01-01
        • 2012-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多