首页 > 解决方案 > 在多线程中创建 QSqlDatabase 连接时如何防止名称冲突

问题描述

我有多线程 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;
    }
}

当我以人类的速度手动检查我的应用程序时,这很好用但是当我运行一个模拟数千个请求的 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.

和服务器因段错误而崩溃

标签: c++multithreadingqt5qsqldatabase

解决方案


即使您使计数器原子化,一个线程仍然可以在DBTask::addDatabase方法中被中断(在创建连接之前),另一个线程可以增加计数器,然后它们都继续并创建 2 个具有相同 ID 的连接。您需要在一个事务中进行这两项操作(增加计数器DBTask::addDatabase创建连接):在内部,通过使用互斥锁。


推荐阅读