在一個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();
不過這種多數據庫連接,應該做一個連接池來管理。