QThreadPool 上的 QSqlDatabase 池

Posted

技术标签:

【中文标题】QThreadPool 上的 QSqlDatabase 池【英文标题】:QSqlDatabase pool on the QThreadPool 【发布时间】:2017-05-18 08:38:41 【问题描述】:

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

现在,使用 QFuture 运行的每个新命令都会创建到我的 mysql 服务器的新 QSqlDatabase 连接。我相信任何与 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 的一些内容?

【问题讨论】:

【参考方案1】:

首先,一个行不通的想法:将连接添加为线程本身的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;
  

【讨论】:

嗨@Kuba,顺便说一句,你最喜欢哪一个?使用 QThreadStorage 还是使用池(下一个)? @Apin 我总是更喜欢少即是多,所以答案应该很明显:) 我只包含了池解决方案来证明正确处理比较棘手。

以上是关于QThreadPool 上的 QSqlDatabase 池的主要内容,如果未能解决你的问题,请参考以下文章

如何覆盖 PyQt5 QThreadPool?

在 QThreadPool 中执行槽

将 MuparserX 与 QThreadPool 一起使用时程序崩溃

QThreadPool 强制停止在 qt cpp

使用 QThreadPool 并行运行单元测试任务

QThreadPool maxThreadCount 在 Application 和 DLL 中不同