从多个线程使用 QSqlQuery
Posted
技术标签:
【中文标题】从多个线程使用 QSqlQuery【英文标题】:Using QSqlQuery from multiple threads 【发布时间】:2017-11-23 14:11:48 【问题描述】:我有很多 C++11 线程在运行,它们有时都需要访问数据库。主要是我初始化数据库连接并打开数据库。 Qt 文档说查询不是线程安全的,所以我使用全局互斥锁,直到线程中存在 QSqlQuery。
这行得通,但保证可以工作还是我有时会遇到问题?
【问题讨论】:
【参考方案1】:看看Documentation告诉我们,那
只能在创建它的线程中使用连接。 在线程之间移动连接或从 不支持不同的线程。
所以你确实需要每个线程一个连接。我通过基于线程生成动态名称解决了这个问题:
auto name = "my_db_" + QString::number((quint64)QThread::currentThread(), 16);
if(QSqlDatabase::contains(name))
return QSqlDatabase::database(name);
else
auto db = QSqlDatabase::addDatabase( "QSQLITE", name);
// open the database, setup tables, etc.
return db;
如果您使用不受 Qt 管理的线程,请使用 QThreadStorage
为每个线程生成名称:
// must be static, to be the same for all threads
static QThreadStorage<QString> storage;
QString name;
if(storage.hasLocalData())
name = storage.localData();
else
//simple way to get a random name
name = "my_db_" + QUuid::createUuid().toString();
storage.setLocalData(name);
重要提示: Sqlite 可能会也可能不会处理多线程。见https://sqlite.org/threadsafe.html。据我所知,嵌入到 Qt 中的 sqlite 是线程安全的,这是默认设置,我在源代码中找不到任何禁用它的标志。但如果您使用的是不同的 sqlite 版本,请确保它确实支持线程。
【讨论】:
如果有一些线程我无法访问,我应该选择什么设计?例如 poco 作为 http 请求处理程序。添加数据库是不可能的,因为我不知道哪个线程调用了处理程序。 我更新了我的答案并添加了示例代码来为非 Qt 线程生成名称 好的,我认为这很清楚。我将为 qsqldatabase 对象创建一个 get 函数,该对象使用线程本地存储来检查数据库是否已添加和打开。如果没有,它将创建数据库并打开它。 没错。这里唯一的“有问题”的部分是关闭数据库连接。我会推荐某种引用计数。如果你愿意,我也可以在我的答案中添加一个例子。 理论上是的,但是一旦线程退出,这不会断开数据库连接!相反,创建一个包装类,在第一次使用时打开连接并在销毁时关闭它。此外,这对于主线程将失败 - 您需要在 QCoreApplication 被破坏之前关闭连接。【参考方案2】:您可以使用 SQL 函数编写类并使用signals-slots 进行查询并从数据库中获取结果。
它是线程安全的,也不需要使用mutex
。
【讨论】:
目前我不使用信号和插槽,而是在锁定全局数据库互斥锁后堆栈上的 QSqlQuery 对象。 看来你没事。如果您与数据库的交互不多/很大,最好为每个查询执行此操作:open(); query(); close();
这意味着我需要先调用 addDatabase 但我在 main 内部调用 addDatabase 并且每个线程都使用没有 db 参数的 QSqlQuery【参考方案3】:
你选择不好的方法。应该使用共享的 QSqlDatabase 对象而不是 QSqlQuery。请检查多线程数据库访问的next example。如果您不清楚,请告诉我。将解释更多。
【讨论】:
从多个线程使用一个数据库连接不是一个好主意,如下所述:doc.qt.io/qt-5/threads-modules.html。 我的例子至少有效。我真的不明白为什么互斥锁也无助于保护数据库连接。 仅仅因为它有效,并不意味着它应该这样做。到目前为止,您可能一直很幸运。文档显然不鼓励这样做,而且您不知道内部发生了什么。此外,互斥锁方法可以防止并行读取,否则可能会发生并行读取(至少在 qt 级别,取决于 sqlite 如何处理它)。但除此之外,这是一个很好的例子。 我将你的答案都投给了@Felix 和 stanislav888,因为它们似乎都有效。 QT 的文档对此有些不清楚,他们说 QSqlDatabase(-constructor-object) 是线程安全的,但不能在线程之间传递。但是,如果它在主线程中定义并由子线程引用,它似乎可以工作。在我的情况下,在一个子线程中定义并传递给另一个子线程时,它无法正常工作。我会跳过互斥锁,因为它没有帮助,它是否是线程安全的:) 编辑:我正在使用 mysql-database 记住:工作!=正确。它总是会在某个时候失败。最坏的情况是会损坏您的数据的静默错误。以上是关于从多个线程使用 QSqlQuery的主要内容,如果未能解决你的问题,请参考以下文章
MFC:如何从主线程使用 MsgWaitForMultipleObjects() 来等待多个线程完成使用 SendMessage()?