应用程序退出时,临界区回收出错导致的进程异常

来源:互联网 发布:基因比对软件 编辑:程序博客网 时间:2024/05/22 07:44
 应用程序退出时,临界区回收出错导致的进程异常。

        问题看起来不是很严重,因为异常仅仅发生在应用程序退出时,此时,即使程序崩溃也没有什么太大的影响。类似问题以前也遇到过,当时由于代码量太多,所以,直接pass。
        异常中断时,函数调用栈如下:
> XX.exe!AfxCriticalTerm()  行 62C++
  XX.exe!_AfxTermAppState()  行 60C++
  XX.exe!doexit(int code=0x00000002, int quick=0x00000000, int retcaller=0x00000000)  行 567C
  XX.exe!exit(int code=0x00000002)  行 393 + 0xd 字节C
  XX.exe!__tmainCRTStartup()  行 284C
  XX.exe!wWinMainCRTStartup()  行 189C


        异常的函数调用:(afxcrit.cpp)

void AFXAPI AfxCriticalTerm()
{
if (_afxCriticalInit)
{
VERIFY(!--_afxCriticalInit);

// delete helper critical sections
DeleteCriticalSection(&_afxLockInitLock);

// delete specific resource critical sections
for (int i = 0; i < CRIT_MAX; i++)
{
#ifdef _DEBUG
ASSERT(!_afxResourceLocked[i]);
#endif
if (_afxLockInit[i])
{
DeleteCriticalSection(&_afxResourceLock[i]);   //中断
VERIFY(!--_afxLockInit[i]);
}
}
}
}

        程序代码:
//类声明
class CBTUpdate
{
public:
CBTUpdate(void);
~CBTUpdate(void);

_DefineClassVariableReference(UINT, CurrentVersion);
_DefineClassVariableReference(FILE_LIST, Files);

_DefineClassVariableReference(CTL_CriticalSection, Lock);  //临界区
_DefineClassVariableReference(BOOL, IsUpdating);

public:
int Load();
int Save();
};

//类实现
CBTUpdate::CBTUpdate(void)
{
m_IsUpdating=FALSE;
}


CBTUpdate::~CBTUpdate(void)
{
}

int CBTUpdate::Load()
{
return 0;
}

int CBTUpdate::Save()
{
return 0;
}

其中,_DefineClassVariableReference是一个宏。因为VC的变量定义很麻烦,一个标准的VC代码定义,应该有一个Get方法和Set方法,然而VC向导没有这样的功能。如果每个变量定义都自己写,那么,上面的四个变量定义,就得近40行代码。所以,改用宏定义的方式,4行代码解决。


        应用程序退出时,调用CBTUpdate::~CBTUpdate()析构函数,随后出现异常。

        根据以上信息,基本上可以确定,由于回收临界区出现问题,导致进程异常!
        于是,我根据这些结论,找出问题代码:
CBTUpdate a;
CBTUpdate b;
b=a;       //问题代码

         是赋值函数出了问题!这时候才恍然大悟。VC 2010和VC6不同,它会给类和结构体加上默认的赋值运算,上面的类,等同于存在这样一个缺省的赋值运算符重载函数:
CBTUpdate & CBTUpdate::operator=(const CBTUpdate & r)
{
m_CurrentVersion=r.m_CurrentVersion;
m_Files=r.m_Files;
m_IsUpdating=r.m_IsUpdating;
m_Lock=r.m_Lock;

return *this;
}

           可以看到,倒霉的事情发生在m_Lock=r.m_Lock;,临界区变量被赋值函数给覆盖了!于是,析构的时候,a析构一次a.m_Lock临界区变量,b也会析构一次b.m_Lock,两次析构就调用两次DeleteCriticalSection函数,同一个临界区被DeleteCriticalSection两次,自然也就发生异常了!

           至此,原因找到。解决方法是重载赋值运算符函数,如下:

CBTUpdate & CBTUpdate::operator=(const CBTUpdate & r)
{
m_CurrentVersion=r.m_CurrentVersion;
m_Files=r.m_Files;
m_IsUpdating=r.m_IsUpdating;

return *this;
}


By:zhanyonhu#@#163.com
原创粉丝点击