在多线程程序中正确使用QSqlDatabase

时间:2013-12-27 01:55:26

标签: c++ multithreading qt qtsql

基于Qt文档:

  

只能在创建连接的线程中使用连接。不支持在线程之间移动连接或从其他线程创建查询。

困扰我的问题是,当我复制构造数据库实例时会发生什么。例如,这是主线程中的代码:

int main(int argc, char** argv) {
...
    QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "DB1");
    db.setHostName("localhost");
...

这里是工作人员线程的连接:

void MyThread::run() {
    QSqlDatabase db(QSqlDatabase::database("DB1"));
    if (db.open()) {
    ...
}

这个线程是否安全?通常,这样的操作在C ++中是安全的,但由于QT使用隐式共享和线程关联,我不再确定。

他们说:连接只能在创建它的线程中使用,但这意味着什么? QSqlDatabase :: addDatabase是指创建连接的位置,还是实际调用 open()函数时的位置。

更新

在Laszlo Papp回答并最终查看Qt源代码之后,我必须说Qt的这部分设计看起来有点不对劲。

如果我理解正确,QSqlDatabase使用隐式共享,但不幸的是它不是真正的隐式共享,因为QSqlDatabase实例的复制构造函数在需要时不会创建共享数据的新实例。更糟糕的是,您无法创建临时连接,而是必须使用静态方法addDatabase / removeDatabase,在这种情况下,您必须同步线程以避免名称冲突。

这当然使得在QtConcurrent中使用QSqlDatabase非常困难,特别是如果连接应该深埋在一些抽象之后。由于我们不知道代码将在哪个线程上运行,因此我们无法在两次调用之间保持连接打开。如果我们想要生成动态数量的任务,我们需要确保任务不使用相同的数据库名称。

所有这些让我对设计目标感到疑惑,以及隐式共享是否适合这种特殊情况。恕我直言,更好的解决方案是让复制构造函数真正做到这一点并为你制作一个连接副本。那些不想拥有私有/临时副本的人仍然可以使用addDatebase / removeDatabase,在这种情况下需要修改方法database()以返回引用。

1 个答案:

答案 0 :(得分:5)

  

他们说:连接只能在创建它的线程中使用,但这意味着什么? QSqlDatabase :: addDatabase是指创建连接的位置,还是实际调用open()函数时的位置。

前者。有关详细信息,请参阅documentation

  

QSqlDatabase类表示与数据库的连接。

     

QSqlDatabase类提供了通过连接访问数据库的接口。 QSqlDatabase的实例表示连接。该连接通过一个受支持的数据库驱动程序提供对数据库的访问,这些驱动程序源自QSqlDriver。或者,您可以从QSqlDriver继承自己的数据库驱动程序。有关详细信息,请参阅如何编写自己的数据库驱动程序。

     

通过调用其中一个静态addDatabase()函数来创建连接(即QSqlDatabase的实例)...

最后一句话应该清楚你的担忧。