在一个QT-Mysql的项目中,偶尔出现连接数据库很慢和连接失败的现象,查看后台能看到这样的警告
Error in my_thread_global_end(): 1 threads didn't exit
后来在qt论坛上看到类似的问题,提问者给出了以下代码,用100个线程同时连接数据再断开,果然能重现问题。
class Client: public QRunnable { public: virtual void run(); }; void Client::run() { QString sID = QString::number((unsigned long long)QThread::currentThreadId()); QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", sID); // unique connection name db.setHostName("localhost"); db.setDatabaseName("test"); db.setUserName("me"); db.setPassword("mypass"); db.open(); // do something query... db.close(); // 对象并未释放,无法remove QSqlDatabase::removeDatabase(sID); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QThreadPool pool; pool.setMaxThreadCount(100); for (int i = 0; i < 100; ++i) { Client *client = new Client; bool started = pool.tryStart(client); Q_ASSERT(started); } pool.waitForDone(); return 0; }
不过先出了另一个问题:removeDatabase: connection '**' is still in use, all queries will cease to work
查了一下,原因是QSqlDatabase对象close()时连接并未完全释放,也就无法remove. 改成指针的形式可以解决
QSqlDatabase *pdb = new QSqlDatabase(); *pdb = QSqlDatabase::addDatabase("QMYSQL", sID); // unique connection name pdb->set...; pdb->open(); pdb->close(); delete pdb; // 释放对象 pdb = NULL; QSqlDatabase::removeDatabase(sID);
解决这个之后,连接数据库很慢和失败、警告都出现了。经过反复尝试,发现QSqlDatabase::addDatabase/open不是线程安全的, 不能被打断,加锁保护后完全正常了。
QMutex gMutexdb; // global mutex for db-open gMutexdb.Lock(); *pdb = QSqlDatabase::addDatabase("QMYSQL", sID); // unique connection name pdb->set...; pdb->open(); gMutexdb.Unlock();
不过这种多数据库连接,应该做一个连接池来管理。