c++ - QThreadPool 上的 QSqlDatabase 池

标签 c++ qt

我正在做一个使用 QSqlDatabase 的项目。现在我正在使用 QtConcurrent (QFuture) 来执行任何 SQL 命令。

现在,使用 QFuture 运行的每个新命令都会创建新的 QSqlDatabase 连接到我的 Mysql 服务器。我相信任何与 Mysql 服务器的新连接都会对握手产生不利影响。因此,我计划创建一个 QSqlDatabase 池,并且从文档 QSqlDatabase 中只能由创建它的线程使用。

所以,我的想法是制作一个 QMap 池,其中 int 是线程 ID,QString 是连接名称。因此,当我想使用 qfuture 从线程池启动线程时,只需从 QMap 池中获取连接名称并获取 QSqlDatabase(此 QSqlDatabase 已连接到服务器)。

示例代码:

//this is static variable
QMap<int, QString> pool;
.....

//At the beginning of sql command to execute
if(pool.contains((int)QThread::currentThreadId()) {
    db = QSqlDatabase::database(pool[(int)QThread::currentThreadId()]);
} else {
    QString key = "someunique" + QString::number((int)QThread::currentThreadId());
    db = QSqlDatabase::add(key)
    ... // some Qsql connection code
    pool.insert((int)QThread::currentThreadId(), key);
}

也许我上面的代码行不通,但我想问的是:我的想法行得通吗?还是我错过了有关 QSqlDatabase 的某些信息?

最佳答案

首先,一个行不通的想法:将连接添加为线程本身的 QObject 属性。它不会工作,因为 QObject 属性系统不是线程安全的。

一个可行的简单想法是使用 QThreadStorage 将数据库连接存储在线程本地存储中。当池中的线程消失时,它将自动处理:

QThreadStorage<QSqlDatabase> connections;

QSqlDatabase newConnection();

QSqlDatabase getConnection() {
  auto & connection = connections.localData();
  if (! connection.isValid())
    connection = newConnection();
  return connection;
}

只要您序列化对池的并发访问,您的想法就会奏效。您还需要确保在线程完成时清理连接。您也可以直接使用 QThread 指针,而不是使用 id。无需通过字符串键引用连接,您可以直接保存它们,因为它们是值。 QSqlDatabase 是句柄,就像文件句柄一样。

QReadWriteLock poolLock;
QMap<QThread*, QSqlDatabase> pool;

struct ConnectionDropper : public QObject {
  void drop() {
    QWriteLocker writeLock{&poolLock};
    pool.remove(qobject_cast<QThread*>(sender()));
  }
}
Q_GLOBAL_STATIC(Dropper, dropper);

QSqlDatabase newConnection();

QSqlDatabase getConnection() {
  auto thread = QThread::currentThread();
  QReadLocker readLock{&poolLock};
  auto it = std::find(pool.begin(), pool.end(), thread);
  if (it != pool.end())
    return it.value();
  readLock.unlock();
  // connecting can take some time, so don't lock the pool while it happens
  auto conn = newConnection();
  // Unique connections to functors are not implemented, thus we need an object.
  QObject::connect(thread, &QThread::finished, &*dropper,
    &ConnectionDropper::drop, Qt::DirectConnection | Qt::UniqueConnection);
  QWriteLocker writeLock{&poolLock};
  pool.insert(thread, conn);
  return conn;
}  

关于c++ - QThreadPool 上的 QSqlDatabase 池,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44042694/

相关文章:

c++ - 使用 auto_ptr 时出现内存损坏错误

c++ - MPI 客户端查找服务器端口失败(MPI_ERR_NAME : invalid name argument)

c++ - 如何将此 SIGNAL 和 SLOT 转换为 qt5 connect 调用?

c++ - Qt5将QImage转为OpenGL格式

c++ - Qt:如何将 textEdit 光标移动到特定的列和行

c++ - 使用 QQmlContext::setContextObject 使 C++ 对象对 QML 可见

c++ - OpenCL 1.2 乱序执行不起作用

javascript - QT/Javascript 桥 : Pass a Qlist<QVariantMap>?

c++ - 未找到 CPP 中来自外部源的函数(Qt)

c++ - PyQt4 OpenGL : Enabling OpenGL Core Profile