4

我是 Qt 开发的新手,它处理线程(信号和插槽)和数据库(以及 SQLite)的方式。我已经开始研究上述技术已经 4 周了。这是我第一次在 SO 上发布问题,我觉得在来找大家之前我已经做了研究。这可能看起来有点长,可能是重复的,但我要求大家在将其作为重复或 tl;dr. 解雇之前通读一遍。

上下文

我正在开发一个在数据库上执行特定操作 X 的 Windows 应用程序。该应用程序是在 Qt 中开发的,并使用 QSQLite 作为数据库引擎。它是一个单线程应用程序,即按顺序处理表。但是,随着数据库大小的增长(表和记录的数量),此处理变得更慢。此操作 X 的结果被写入同一 DB 中的单独结果表中。正在完成的处理对问题无关紧要,但基本而言,它的作用如下:


从 Table_X_1 中读取一行 从 Table_X_2 中读取一行
对行进行一些操作(仅读取)
将结果推送到 Table_X_Results 表中(这是对 DB 执行的唯一写入)

Table_X_1 和 Table_X_2 的列数和类型以及行数相同,只是数据可能不同。

我正在尝试做的事情

为了提高性能,我正在尝试使应用程序多线程。最初我产生了两个线程(使用 QtConcurrentRun)。这两个表可以分为两种类型,例如 A 和 B。每个线程将处理两种类型的表。线程内的处理保持不变,即,在每个线程内,表是按顺序处理的。

该函数是这样的,它使用 SELECT 来获取行进行处理,并使用 INSERT 将结果插入结果表中。为了插入我正在使用事务的结果。

在开始实际操作之前,我正在创建所有中间表、结果表和索引。我每次都在打开和关闭连接。对于线程,我在进入循环之前创建并打开一个连接(每个线程一个)。

问题

在我的处理函数中,我得到以下(讨厌的、臭名昭著的、顽固的)错误:

QSqlError(5, "无法获取行", "数据库被锁定")

当我尝试从 DB 中读取一行时(使用 SELECT),我收到了这个错误。这与我在结果表中执行 INSERT 的功能相同。SELECT 和 INSERT 在同一个事务中(开始和提交对)。对于 INSERT,我使用的是准备好的语句 (SQLiteStatement)。

我正在做的看似奇怪的事情的原因

  1. 我正在使用 QtConcurrentRun 创建线程,因为它很简单!我尝试过使用 QThread(不是子类 QThread,而是一种方法)。这也会导致同样的问题。
  2. 我正在使用 DSQLITE_THREADSAFE=0 进行编译以避免应用程序崩溃。如果我使用默认值 (DSQLITE_THREADSAFE=1),我的应用程序会在 SQLiteStatement::recordSet->Reset() 处崩溃。此外,使用默认选项,内部 SQLITE 同步机制开始发挥作用,这可能不可靠。如果需要,我将使用显式同步。
  3. 使应用程序多线程以提高性能,而不是这样。我正在处理那里推荐的所有优化。
  4. 使用 QSqlDatabase::setConnectOptions 和 QSQLITE_BUSY_TIMEOUT=0。一个链接表明它将防止数据库立即被锁定,因此可能会给我的线程适当的时间来“和平地死去”。这失败了:数据库比以前更频繁地被锁定。

观察

  1. 数据库仅在其中一个线程返回时才进入锁定状态。这种行为是一致的。
  2. 当使用 DSQLITE_THREADSAFE=1 进行编译时,应用程序会在其中一个线程返回时崩溃。在我的函数中的 SQLiteStatement::recordSet->Reset() 和 sqlite3.c 中的 winMutexEnter()(从 EnterCriticalSection() 调用)调用堆栈点。这也是一致的。
  3. 使用 QtConcurrentRun 创建的线程不会立即死亡。
  4. 如果我使用 QThreads,我无法让它们返回。也就是说,即使我正确连接了信号和插槽,我也感觉线程永远不会返回。等待线程的正确方法是什么,它们需要多长时间才能死掉?
  5. 完成执行的线程永远不会返回,它已锁定数据库并因此锁定错误。
  6. 我检查了 SQLITE_BUSY 并试图让线程休眠但无法让它工作。在 Qt 中睡眠的正确方法是什么(对于使用 QtConcurrentRun 或 QThreads 创建的线程)?
  7. 当我关闭连接时,我收到以下警告:

    QSqlDatabasePrivate::removeDatabase: 连接 'DB_CONN_CREATE_RESULTS' 仍在使用中,所有查询都将停止工作。

    这有什么意义吗?一些链接表明此警告是由于使用本地 QSqlDatabase 而出现的,如果连接是类成员则不会出现。但是,这可能是我的问题的原因吗?

进一步的实验

  1. 我正在考虑创建另一个仅包含结果表(Table_X_Results)的数据库。基本原理是,虽然线程将从一个数据库(我目前拥有的数据库)读取,但它们将写入另一个数据库。但是,我可能仍然面临同样的问题。此外,我在论坛和维基上读到,可以有两个线程在同一个数据库上进行读写。那么为什么我不能让这个场景起作用呢?
  2. 我目前正在使用 SQLITE 版本 3.6.17。这可能是问题吗?如果我使用 3.8.5 版本会更好吗?

我试图发布我已经探索过的网络资源,但我收到一条消息说“我需要 10 个代表才能发布超过 2 个链接”。任何帮助/建议将不胜感激。

4

0 回答 0