首页 > 解决方案 > 为 Qt 工作线程打开和关闭数据库连接的正确方法是什么

问题描述

我正在处理一个我们想要在数据库表上异步执行INSERT/DELETE语句的场景(这是一个即发即弃的场景)。我打算简单地用相关数据触发一个信号,并让线程的事件循环处理每个信号,类似于以下示例:

Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(signalSource, &SignalSource::dataReady, worker, &Worker::updateMyFancyTable);
workerThread.start();

只要线程正在运行,它就应该打开自己的数据库连接。我会这样做(当然,增加了错误处理):

connect(&workerThread, &QThread::started, worker, &Worker::establishDatabaseConnection);

当停止线程时,它应该完成它的工作,然后关闭数据库连接。

据我了解,在线程上调用quit()将首先处理所有剩余的信号(如果有),然后退出线程的事件循环。

在这种情况下如何正确关闭数据库连接?

  1. 在调用之前我会发出关闭连接的信号quit()吗?
  2. 我是否在发出一次调用的主线程的插槽中关闭连接QThread::finished
  3. 还有什么?

标签: qtqthreadqsqldatabase

解决方案


我认为这个讨论回答了你的一些问题:立即停止在 QThread.exit() 上处理事件队列。所以看起来是的,所有的插槽都是在线程退出之前执行的。建议的解决方案是使用 QThread::requestInterruption 并在您的插槽中测试 QThread::currentThread()->isInterruptionRequested() 。请注意,该插槽仍被多次调用,因此您必须返回,直到队列为空。

在您的情况下,您可以在中断请求后忽略呼叫并发出信号以关闭连接。所以选项1可能是一个解决方案。或者您甚至可以在 isInterruptionRequested() 第一次在您的插槽中返回 true 时关闭连接,但您可能必须处理空事件队列的情况并无论如何调用插槽。

选项 2 可能不是一个可行的解决方案,具体取决于您如何处理与数据库的连接:通常,该连接只能在创建它的线程中使用。Qt SQL 模块就是这种情况(https://doc.qt.io/qt-5/threads-modules.html#threads-and-the-sql-module)。您可以将 finished() 信号连接到在后台线程中执行的插槽,然后关闭那里的连接,但我对https://doc.qt.io/qt-5/qthread.html#finished的理解是可能是不可能的,因为“当这个信号发出时,事件循环已经停止运行”。奇怪的事情:我测试并在后台线程中调用了插槽......文档说“除了延迟删除事件”,所以在后台线程中调用了 dtor,这将是一个选项。

对于 QSqlDatabase(或以相同方式工作的连接),我更喜欢手动处理事件队列,以获得更多控制权。例如,创建一个消息队列,等待新请求的信号量并在 QThread 子类中处理这些请求。在 run() 方法中,首先创建连接,在执行期间使用,并在从 run() 方法返回之前关闭。run() 方法迭代直到请求中断。这使得对象独立,管理自己的数据库连接。这似乎更简单,特别是当您想同时查询数据库时。


推荐阅读