17

基于 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() 以返回引用。

4

2 回答 2

5

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

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

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

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

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

最后一句话应该可以消除您的担忧。

于 2013-12-27T02:04:53.217 回答
4

您可以QSqlDatabase::cloneDatabase用于获取数据库的“真实”副本,该副本可以在任何线程中打开。

您需要在初始化克隆数据库的线程中执行此操作,但您可以将获得的尚未打开的数据库移动到任何线程并在那里使用它。

于 2015-09-11T09:15:38.767 回答