在多线程中创建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();
    ...
}