QThreadPool上的QSqlDatabase池

时间:2017-05-18 08:38:42

标签: c++ qt

我正在做一个使用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 个答案:

答案 0 :(得分:0)

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