【2011-04-30 11:59:11记录于新浪博客,转载】
昨天在做项目测试时突然发现一个莫名异常。异常的现象很奇怪,表现在我的SQLite数据库更新时报死锁异常。
从数据库的死锁,你能想到什么?虽然缺乏充分理由,我还是首先怀疑是更新数据格式的问题,可是测试到半夜,没发现任何问题。今天早上进一步思考:数据库的死锁要有死锁条件,google结果是,SQLite容易在多进程并发时死锁。多进程?哪里出来的多进程?
进一步检查数据库操作。我的数据库操作封装在一个singleton单例类中,并通过c++的auto_ptr实现。所有的数据库操作都通过这个唯一接口进出。
static std::auto_ptr<CDBClass> m_hInstance;
static CDBClass* CDBClass::getInstance()
{
if (m_hInstance.get() == 0) {
m_hInstance = std::auto_ptr<CDBClass>(new CDBClass()); (1)
}
return m_hInstance.get();
}
数据库的连接在CDBClass的构造函数中实现。
CDBClass::CDBClass()
{
m_db.open();
……
}
在项目其它代码中通过以下代码引用:
CDBClass* dbclass = CDBClass::getInstance(); (2)
根据SQLite死锁条件的分析,出现死锁的最大可能是试图对已打开数据库再次执行连接操作。可是,数据库的操作是单例模式,数据库连接操作所在的构造函数只应该被执行一次才对。
经过代码调试,发现代码(1)被先后调用了两次。也就是说,CDBClass的静态变量m_hInstance并没有保证唯一性,在系统执行过程中,它发生了一些奇怪的变化。
通过断点处的调用堆栈,发现第一次的代码(1)调用,发生在类A的构造函数中,而类A被定义为一个全局变量。这很正常,因为全局变量就是在第一时间内初始化的。可这些,又有什么关系呢?反复的思考所有可能的调用路径,想的脑袋都疼,依然百思不得其解。问题的焦点在于,m_hInstance在第一次初始化之后,其持有的模板指针怎么突然没了?
一个念头突然闪过,m_hInstance是个静态变量,其初始化过程也是发生在第一时间,它的初始化也会导致模板指针为空。难道,它的初始化发生在全局变量类A之后?赶紧调试,跟踪m_hInstance的初始化。
结果果真如此。
全局类A的初始化,先于静态变量m_hInstance发生。所以,当A实例化了时,m_hInstance先持有了模板类的指针;随后,m_hInstnace自身的初始化又把这个指针置为空,这也就是为什么代码(1)被又一次执行的原因。
解决办法:
在A的构造函数中移除对CDBClass::getInstance()的调用,问题解决。