在多线程中创建QSqlDatabase连接时如何防止名称冲突
How to prevent name collisions when creating QSqlDatabase connection in multiple threads
我有多线程 QTcpServer,对于每个数据库请求,它都会创建新线程以保持服务器响应。所以在每个线程中我都必须创建新的 QSqlDatabase 连接。但是我不断收到连接之间的名称冲突。
这是我重现问题的示例代码。:-
#include <QSqlDatabase>
class DBTask
{
public:
DBTask(ClientSocket *socket,ConnectionWorker *connectionWorker);
~DBTask();
static void initStatic();
private:
static QThreadPool *pool; // all addConnection() call be be called in QtConcurrent::run with this pool
static QString host, user, type, password, name;
static quint64 dbConnectionNumber;
QSqlDatabase db;
ClientSocket *socket;
ConnectionWorker *connectionWorker;
bool addDatabase() ;
};
quint64 DBTask::dbConnectionNumber=0;
DBTask::DBTask(ClientSocket *socket, ConnectionWorker *connectionWorker):
socket(socket),
connectionWorker(connectionWorker)
{
dbConnectionNumber++;
}
bool DBTask::addDatabase() {
QSqlDatabase db = QSqlDatabase::addDatabase(type,QString::number(dbConnectionNumber));
db.setHostName(host);
db.setDatabaseName(name);
db.setUserName(user);
db.setPassword(password);
if(!db.open()){
qWarning() << "Error while opening database for socket " << socket << '\n' << db.lastError();
return false;
}
else {
return true;
}
}
当我用 GUI 以人的速度手动检查我的应用程序时,这工作正常但是当我 运行 模拟数千个请求的 c++ 测试代码时:-
void connectionTest(){
QThreadPool pool;
pool.setMaxThreadCount(10);
for(int i=0;i<10;i++){
QtConcurrent::run(&pool,[this](){
for(int i=0;i<1000;i++){
login(i%2); // login function sends request to QTcpServer
}
});
}
}
我收到多个这样的错误:-
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
并且服务器因段错误而崩溃
即使你使计数器成为原子计数器,一个线程仍然可以在 DBTask::addDatabase
方法中被中断(在创建连接之前),另一个线程可以增加计数器然后它们都继续并创建 2 个连接同一个身份证。您需要在一个事务中进行这两项操作(增加计数器 和 连接创建):在 DBTask::addDatabase
内,通过使用互斥锁。
添加QMutex到addDatabase后,生效了:-
bool DBTask::addDatabase() {
mutex.lock();
dbConnectionNumber++;
db = QSqlDatabase::addDatabase(type,QString::number(dbConnectionNumber));
mutex.unlock();
...
}
我有多线程 QTcpServer,对于每个数据库请求,它都会创建新线程以保持服务器响应。所以在每个线程中我都必须创建新的 QSqlDatabase 连接。但是我不断收到连接之间的名称冲突。
这是我重现问题的示例代码。:-
#include <QSqlDatabase>
class DBTask
{
public:
DBTask(ClientSocket *socket,ConnectionWorker *connectionWorker);
~DBTask();
static void initStatic();
private:
static QThreadPool *pool; // all addConnection() call be be called in QtConcurrent::run with this pool
static QString host, user, type, password, name;
static quint64 dbConnectionNumber;
QSqlDatabase db;
ClientSocket *socket;
ConnectionWorker *connectionWorker;
bool addDatabase() ;
};
quint64 DBTask::dbConnectionNumber=0;
DBTask::DBTask(ClientSocket *socket, ConnectionWorker *connectionWorker):
socket(socket),
connectionWorker(connectionWorker)
{
dbConnectionNumber++;
}
bool DBTask::addDatabase() {
QSqlDatabase db = QSqlDatabase::addDatabase(type,QString::number(dbConnectionNumber));
db.setHostName(host);
db.setDatabaseName(name);
db.setUserName(user);
db.setPassword(password);
if(!db.open()){
qWarning() << "Error while opening database for socket " << socket << '\n' << db.lastError();
return false;
}
else {
return true;
}
}
当我用 GUI 以人的速度手动检查我的应用程序时,这工作正常但是当我 运行 模拟数千个请求的 c++ 测试代码时:-
void connectionTest(){
QThreadPool pool;
pool.setMaxThreadCount(10);
for(int i=0;i<10;i++){
QtConcurrent::run(&pool,[this](){
for(int i=0;i<1000;i++){
login(i%2); // login function sends request to QTcpServer
}
});
}
}
我收到多个这样的错误:-
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
QSqlDatabasePrivate::removeDatabase: connection '10' is still in use, all queries will cease to work.
QSqlDatabasePrivate::addDatabase: duplicate connection name '10', old connection removed.
并且服务器因段错误而崩溃
即使你使计数器成为原子计数器,一个线程仍然可以在 DBTask::addDatabase
方法中被中断(在创建连接之前),另一个线程可以增加计数器然后它们都继续并创建 2 个连接同一个身份证。您需要在一个事务中进行这两项操作(增加计数器 和 连接创建):在 DBTask::addDatabase
内,通过使用互斥锁。
添加QMutex到addDatabase后,生效了:-
bool DBTask::addDatabase() {
mutex.lock();
dbConnectionNumber++;
db = QSqlDatabase::addDatabase(type,QString::number(dbConnectionNumber));
mutex.unlock();
...
}