简体   繁体   English

有什么方法可以使Enter / LeaveCriticalSection留下句柄

[英]Is there any way that Enter/LeaveCriticalSection could leave a handle behind

I have the following code in my program: 我的程序中包含以下代码:

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

It works fine 99.999% of the time but occasionally a handle seems to get left behind. 它可以在99.999%的时间内正常工作,但有时似乎会留下手柄。 Now I have done the obvious things like make sure that x did not change value between the enter and make sure that there isn't any "return" or "break" inside "// stuff" but I was wondering if there could be something else that would cause an enter/leave pair to leave a handle behind. 现在,我做了一些显而易见的事情,例如确保x在输入之间不改变值,并确保在“ //东西”中没有任何“返回”或“中断”,但是我想知道是否可能有东西否则会导致输入/离开对留下手柄。 Perhaps running out of memory or overflowing some counter in the OS or whatever. 可能是内存不足或操作系统中的某些计数器溢出或其他原因。

EDIT: I am new to C++, the program has only recently been converted from C. It has no exceptions anywhere in the entire program. 编辑:我是C ++的新手,该程序直到最近才从C转换。它在整个程序的任何地方都没有异常。

If you don't explicitly delete the critical section and if there was ever contention on the critical section, you will leak a handle. 如果您没有明确删除关键部分,并且关键部分曾经有过争用,您将泄漏句柄。 Some implementations of critical sections on Windows allocate a semaphore when two or more threads overlap in their attempts to enter a single critical section. 当两个或多个线程尝试进入单个关键部分时重叠时,Windows关键部分的某些实现会分配信号量。

It's not a leak. 这不是泄漏。 Or rather, it isn't a leak if the number of "leaked" handles is less than or equal to the number of global critical sections you are using. 更确切地说,如果“泄漏的”句柄的数量小于或等于您正在使用的全局关键节的数量,这并不是泄漏。

Mick, 米克

There are a number of things that could be happening. 有很多事情可能发生。

Exceptions can cause control flow to exit the block before executing the LeaveCriticalSection call. 异常可能导致控制流在执行LeaveCriticalSection调用之前退出该块。 To avoid this problem you can wrap up the entering & exiting of the critical section within a stack based object using the Resource Acquisition Is Initialisation (RAII) pattern. 为避免此问题,您可以使用“资源获取为初始化(RAII)”模式来包装关键部分在基于堆栈的对象中的进入和退出。

Without a more complete listing, however, it is impossible to say whether there are any other issues with your code. 但是,如果没有更完整的清单,就无法说出代码是否还有其他问题。

Cheers Seb 干杯

Since you're in C++ land, an exception leaving that // stuff part will skip LeaveCriticalSection() . 由于您在C ++领域,因此// stuff部分的异常将跳过LeaveCriticalSection() Look up RAII ("Resource-Aquisition-Is-Initialization") as a tool to prevent that from happening. 查找RAII(“资源获取即初始化”)作为防止这种情况发生的工具。 Here's a somewhat simple example for such a class: 这是此类的一个简单示例:

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()

On a side-note: Deadlocks can sometimes give the impression of some lock not being properly unlocked. 附带说明:死锁有时会给人以某些锁没有正确解锁的印象。

The most probable cause is an exception. 最可能的原因是一个例外。 Are you catching the exceptions inside this function and whether they call Leave or not? 您是否正在捕获此函数中的异常以及它们是否调用Leave? Also, note that it is better to use CSingleLock class to lock the critical section instead of using raw APIs like this. 另外,请注意,最好使用CSingleLock类来锁定关键部分,而不是使用像这样的原始API。 By using CSingleLock you can guantee proper cleanup incase of exceptions. 通过使用CSingleLock,您可以在发生异常的情况下进行适当的清理。

Expanding on sbi's answer (as you say you're new to C++), an exception ignores the rest of the code from the place where it is invoked until it reaches a place where the exception can be handled (a 'catch') - the only exception to this is when the exception wraps up memory in a stack - it calls the destructors of stack variables. 扩展sbi的答案(正如您所说的,您是C ++的新手),异常会忽略从调用它的地方开始的其余代码,直到到达可以处理异常的地方(“捕获”)为止-唯一的例外是当异常将内存包装在堆栈中时-它调用堆栈变量的析构函数。

To ensure that the 'Leave' is always called, use the following class: (excuse the lack of formatting), and put both an instance of this class and the critical code in a new stack. 为了确保始终调用“ Leave”,请使用以下类:(缺少格式),然后将此类的实例和关键代码放入新的堆栈中。 This ensures that the 'Leave' is always called, both in non-exception and exception scenarios. 这样可以确保在非异常和异常情况下都始终调用“ Leave”。

edit: Updating poc code to reflect comment. 编辑:更新POC代码以反映评论。

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

calling place: 呼叫地点:

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

您是否有可能因输入/离开不匹配而丢失了句柄,而是忘记了调用DeleteCriticalSection。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM