windows VC++ 2010:奇怪的临界区错误
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7123755/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
VC++ 2010: Weird Critical Section error
提问by dario_ramos
My program is randomly crashing in a small scenario I can reproduce, but it happens in mlock.c (which is a VC++ runtime file) from ntdll.dll, and I can't see the stack trace. I do know that it happens in one of my thread functions, though.
我的程序在我可以重现的一个小场景中随机崩溃,但它发生在 ntdll.dll 的 mlock.c(这是一个 VC++ 运行时文件)中,我看不到堆栈跟踪。不过,我确实知道它发生在我的一个线程函数中。
This is the mlock.c code where the program crashes:
这是程序崩溃的 mlock.c 代码:
void __cdecl _unlock (
int locknum
)
{
/*
* leave the critical section.
*/
LeaveCriticalSection( _locktable[locknum].lock );
}
The error is "invalid handle specified". If I look at locknum, it's a number larger than _locktable's size, so this makes some sense.
错误是“指定的句柄无效”。如果我查看 locknum,它是一个大于 _locktable 大小的数字,所以这是有道理的。
This seems to be related to Critical Section usage. I do use CRITICAL_SECTIONS in my thread, via a CCriticalSection wrapper class and its associated RAII guard, CGuard. Definitions for both hereto avoid even more clutter.
这似乎与临界区的使用有关。我确实在我的线程中使用了 CRITICAL_SECTIONS,通过一个 CCriticalSection 包装类及其关联的 RAII 保护,CGuard。此处对两者的定义以避免更加混乱。
This is the thread function that's crashing:
这是崩溃的线程函数:
unsigned int __stdcall CPlayBack::timerThread( void * pParams ) {
#ifdef _DEBUG
DRA::CommonCpp::SetThreadName( -1, "CPlayBack::timerThread" );
#endif
CPlayBack * pThis = static_cast<CPlayBack*>( pParams );
bool bContinue = true;
while( bContinue ) {
float m_fActualFrameRate = pThis->m_fFrameRate * pThis->m_fFrameRateMultiplier;
if( m_fActualFrameRate != 0 && pThis->m_bIsPlaying ) {
bContinue = ( ::WaitForSingleObject( pThis->m_hEndThreadEvent, static_cast<DWORD>( 1000.0f / m_fActualFrameRate ) ) == WAIT_TIMEOUT );
CImage img;
if( pThis->m_bIsPlaying && pThis->nextFrame( img ) )
pThis->sendImage( img );
}
else
bContinue = ( ::WaitForSingleObject( pThis->m_hEndThreadEvent, 10 ) == WAIT_TIMEOUT );
}
::GetErrorLoggerInstance()->Log( LOG_TYPE_NOTE, "CPlayBack", "timerThread", "Exiting thread" );
return 0;
}
Where does CCriticalSection
come in? Every CImage
object contains a CCriticalSection
object which it uses through a CGuard
RAII lock. Moreover, every CImage
contains a CSharedMemory
object which implements reference counting. To that end, it contains two CCriticalSection
's as well, one for the data and one for the reference counter. A good example of these interactions is best seen in the destructors:
从哪里CCriticalSection
进来?每个CImage
对象都包含一个CCriticalSection
它通过CGuard
RAII 锁使用的对象。此外,每个都CImage
包含一个CSharedMemory
实现引用计数的对象。为此,它也包含两个CCriticalSection
,一个用于数据,一个用于引用计数器。在析构函数中可以最好地看到这些交互的一个很好的例子:
CImage::~CImage() {
CGuard guard(m_csData);
if( m_pSharedMemory != NULL ) {
m_pSharedMemory->decrementUse();
if( !m_pSharedMemory->isBeingUsed() ){
delete m_pSharedMemory;
m_pSharedMemory = NULL;
}
}
m_cProperties.ClearMin();
m_cProperties.ClearMax();
m_cProperties.ClearMode();
}
CSharedMemory::~CSharedMemory() {
CGuard guardUse( m_cs );
if( m_pData && m_bCanDelete ){
delete []m_pData;
}
m_use = 0;
m_pData = NULL;
}
Anyone bumped into this kind of error? Any suggestion?
有人遇到过这种错误吗?有什么建议吗?
Edit: I got to see some call stack: the call comes from ~CSharedMemory. So there must be some race condition there
编辑:我看到了一些调用堆栈:调用来自~CSharedMemory。所以那里必须有一些竞争条件
Edit: More CSharedMemory code here
编辑:更多 CSharedMemory 代码在这里
采纳答案by dario_ramos
I decided to adhere to the KISS principle and rock and roll all nitesimplify things. I figured I'd replace the CSharedMemoryClass
with a std::tr1::shared_ptr<BYTE>
and a CCriticalSection
which protects it from concurrent access. Both are members of CImage
now, and concerns are better separated now, IMHO.
我决定坚持 KISS 原则并 整夜摇滚简化事情。我想我会CSharedMemoryClass
用 astd::tr1::shared_ptr<BYTE>
和 a替换它,以CCriticalSection
保护它免受并发访问。两者都是CImage
现在的成员,恕我直言,现在最好分开关注。
That solved the weird critical section, but now it seems I have a memory leak caused by std::tr1::shared_ptr
, you might see me post about it soon... It never ends!
这解决了奇怪的临界区,但现在看来我有一个由 引起的内存泄漏std::tr1::shared_ptr
,你可能很快就会看到我发布它......它永远不会结束!
回答by John Dibling
The "invalid handle specified" return code paints a pretty clear picture that your critical section object has been deallocated; assuming of course that it was allocated properly to begin with.
“指定的无效句柄”返回代码描绘了一幅非常清晰的画面,表明您的临界区对象已被释放;当然,假设它一开始就被正确分配。
Your RAII class seems like a likely culprit. If you take a step back and think about it, your RAII class violates the Sepration Of Concernsprinciple, because it has two jobs:
您的 RAII 课程似乎是罪魁祸首。如果你退后一步想一想,你的 RAII 类违反了关注点分离原则,因为它有两个工作:
- It provides allocate/destroy semantics for the CRITICAL_SECTION
- It provides acquire/release semantics for the CRITICAL_SECTION
- 它为 CRITICAL_SECTION 提供分配/销毁语义
- 它为 CRITICAL_SECTION 提供获取/释放语义
Most implementations of a CS wrapper I have seen violate the SoC principle in the same way, but it can be problematic. Especially when you have to start passing around instances of the class in order to get to the acquire/release functionality. Consider a simple, contrived example in psudocode:
我见过的大多数 CS 包装器的实现都以同样的方式违反了 SoC 原则,但它可能会出现问题。特别是当您必须开始传递类的实例以获取获取/释放功能时。考虑在伪代码中的一个简单的、人为的例子:
void WorkerThreadProc(CCriticalSection cs)
{
cs.Enter();
// MAGIC HAPPENS
cs.Leave();
}
int main()
{
CCriticalSection my_cs;
std::vector<NeatStuff> stuff_used_by_multiple_threads;
// Create 3 threads, passing the entry point "WorkerThreadProc"
for( int i = 0; i < 3; ++i )
CreateThread(... &WorkerThreadProc, my_cs);
// Join the 3 threads...
wait();
}
The problem here is CCriticalSection
is passed by value, so the destructor is called 4 times. Each time the destructor is called, the CRITICAL_SECTION is deallocated. The first time works fine, but now it's gone.
这里的问题CCriticalSection
是通过值传递,所以析构函数被调用了 4 次。每次调用析构函数时,都会释放 CRITICAL_SECTION。第一次工作正常,但现在它消失了。
You could kludge around this problem by passing references or pointers to the critical section class, but then you muddy the semantic waters with ownership issues. What if the thread that "owns" the crit sec dies before the other threads? You could use a shared_ptr
, but now nobody really "owns" the critical section, and you have given up a little control in on area in order to gain a little in another area.
您可以通过向临界区类传递引用或指针来解决这个问题,但随后您将语义水域与所有权问题混为一谈。如果“拥有”暴击秒的线程在其他线程之前死亡怎么办?您可以使用 a shared_ptr
,但现在没有人真正“拥有”临界区,并且您已经放弃了对某个区域的一点控制权,以便在另一个区域获得一点控制权。
The true "fix" for this problem is to seperate concerns. Have one class for allocation & deallocation:
这个问题的真正“修复”是分离关注点。有一类用于分配和释放:
class CCriticalSection : public CRITICAL_SECTION
{
public:
CCriticalSection(){ InitializeCriticalSection(this); }
~CCriticalSection() { DestroyCriticalSection(this); }
};
...and another to handle locking & unlocking...
...和另一个处理锁定和解锁...
class CSLock
{
public:
CSLock(CRITICAL_SECTION& cs) : cs_(cs) { EnterCriticalSection(&cs_); }
~CSLock() { LeaveCriticalSection(&cs_); }
private:
CRITICAL_SECTION& cs_;
};
Now you can pass around raw pointers or references to a single CCriticalSection object, possibly const, and have the worker threads instantiate their own CSLocks on it. The CSLock is owned by the thread that created it, which is as it should be, but ownership of the CCriticalSection is clearly retained by some controlling thread; also a good thing.
现在,您可以传递原始指针或对单个 CCriticalSection 对象(可能是 const)的引用,并让工作线程在其上实例化自己的 CSLock。CSLock 归创建它的线程所有,这是应该的,但是 CCriticalSection 的所有权显然由某些控制线程保留;也是一件好事。
回答by Ajay
- Make sure Critical Section object is not in
#pragma
packing 1 (or any non-default packing). - Ensure that no other thread (or same thread) is corrupting the CS object. Run some static analysis tool to check for any buffer overrun problem.
- If you have runtime analysis tool, do run it to find the issue.
- 确保关键部分对象不在
#pragma
包装 1(或任何非默认包装)中。 - 确保没有其他线程(或同一线程)破坏 CS 对象。运行一些静态分析工具来检查任何缓冲区溢出问题。
- 如果您有运行时分析工具,请运行它以查找问题。