如何在多线程应用程序中使用SQLite?

Eda*_*aor 43 c++ sqlite multithreading

我正在开发一个使用SQLite作为数据库的应用程序,并且在理解如何在多个线程中使用它时遇到一些麻烦(遗憾的是,没有其他Stack Overflow问题对我有帮助).

我的用例:数据库有一个表,我们称之为"A",它有不同的行组(基于其中一个列).我有应用程序的"主线程",它从表A中读取内容.此外,我偶尔决定更新某一组行.为此,我想生成一个新线程,删除该组的所有行,然后重新插入它们(这是在我的应用程序上下文中执行此操作的唯一方法).这可能同时发生在不同的组中,因此我可能有2个以上的线程尝试更新数据库.

我在每个线程的更新周期开始时使用来自每个线程,IE的不同事务,我有一个开始.实际上,每个线程实际上做的是调用"BEGIN",从数据库中删除它需要"更新"的所有行,然后使用新值再次插入它们(这是必须在我的上下文中完成的方式)应用).

现在,我试图了解如何实现这一点.我试过阅读(Stack Overflow上的其他答案,SQLite网站),但我还没有找到所有的答案.以下是我想知道的一些事情:

  1. 我是否需要调用"open"并从每个线程创建一个新的sqlite结构?
  2. 我是否需要为所有这些添加任何特殊代码,或者是否足以生成不同的线程,更新行,这很好(因为我使用不同的事务)?
  3. 我看到有些东西在讨论不同的锁类型,以及我可能会因调用某些API而收到"SQLite忙"的事实,但老实说,当我需要考虑所有这些时,我没有看到任何完全解释的引用.我需要吗?

如果有人能够回答问题/指出我的良好资源方向,我将非常感激.

更新1:从我到目前为止阅读的所有内容看来,无论如何你都不能有两个线程要写入数据库文件.

请参阅:http://www.sqlite.org/lockingv3.html.在3.0节中:RESERVED锁意味着该进程计划在将来的某个时刻写入数据库文件,但它当前只是从文件中读取.虽然多个SHARED锁可以与单个RESERVED锁共存,但一次只能激活一个RESERVED锁.

这是否意味着我每次只能生成一个线程来更新一组行?即,有某种轮询线程决定我需要更新一些行,然后创建一个新线程来执行它,但一次不会超过一个?因为它看起来像我创建的任何其他线程将只获得SQLITE_BUSY,直到第一个线程完成,无论如何.

我理解正确吗?

顺便说一句,感谢到目前为止的答案,他们帮助了很多.

Sna*_*zer 28

开始使用SQLlite进行多线程使用时的一些步骤:

  1. 确保使用多线程标志编译sqlite.
  2. 您必须在sqlite文件上调用open以在每个线程上创建连接,不要在线程之间共享连接.
  3. SQLite有一个非常保守的线程模型,当你执行写操作时,包括打开即将执行INSERT/UPDATE/DELETE的事务,其他线程将被阻塞,直到此操作完成.
  4. 如果不使用事务,则事务是隐式的,因此如果启动INSERT/DELETE/UPDATE,sqlite将尝试获取独占锁,并在释放之前完成操作.
  5. 如果您执行BEGIN EXCLUSIVE语句,它将在该事务中执行操作之前获取独占锁.COMMIT或ROLLBACK将释放锁定.
  6. 您的sqlite3_step,sqlite3_prepare和其他一些调用可能会返回SQLITE_BUSY或SQLITE_LOCKED.SQLITE_BUSY通常意味着sqlite需要获取锁.两个返回值之间的最大区别:
    • SQLITE_LOCKED:如果你从一个sqlite3_step声明得到这个,你必须调用语句句柄sqlite3_reset.你应该只在第一次调用sqlite3_step时得到这个,所以一旦调用reset,你实际上可以"重试"你的sqlite3_step调用.在其他操作上,它与SQLITE_BUSY相同
    • SQLITE_BUSY:没有必要sqlite3_reset打电话,只重试等待位的锁被释放后您的操作.

  • SQLITE_LOCKED 与多线程锁定无关。这通常是在 SELECT 表仍处于活动状态时调用 DROP 表时遇到的编程错误。你真的需要关心 SQLITE_BUSY http://www.sqlite.org/cvstrac/wiki?p=DatabaseIsLocked (2认同)

Kri*_*ian 22

看看这个链接.最简单的方法是自己进行锁定,并避免共享线程之间的连接.可以在这里找到另一个好的资源,它的结论是:

  1. 确保使用-DTHREADSAFE = 1编译SQLite.

  2. 确保每个线程打开数据库文件并保留自己的sqlite结构.

  3. 确保您在同时访问db文件时处理一个或多个线程可能发生冲突的可能性:正确处理SQLITE_BUSY.

  4. 确保在事务中包含修改数据库文件的命令,如INSERT,UPDATE,DELETE等.

  • "自己锁定":确保线程不会通过锁定互斥锁(当然它们必须共享)来同时读取和写入数据库.我的观点是,使用你熟悉和理解的库自己做这件事可能更容易,而不是依赖于Sqlite的不那么清晰的锁定语义(这又取决于你编译它的标志). (3认同)
  • 你到底是什么意思“自己锁定”?IE,只需确保我自己没有两个线程同时写入数据库?如果是这样,我可以允许两个不同的线程同时读取数据库而不会遇到任何问题吗? (2认同)
  • @dalle它不会使它过时,因为它序列化了对数据库的所有访问.相反,如果您有多个线程,每个线程都有自己的sqlite结构,那么它们将能够同时从数据库中读取.然而,它确实减轻了"自己锁定"的需要. (2认同)
  • 换句话说,没有办法在多线程程序中有效地使用SQLITE. (2认同)

小智 9

我意识到这是一个旧线程并且响应很好,但我最近一直在研究这个问题,并且对一些不同的实现进行了有趣的分析.主要是它弥补了连接共享,消息传递,线程本地连接和连接池的优缺点.请看这里:http://dev.yorhel.nl/doc/sqlaccess


Nik*_*ita 5

SQLite 的现代版本默认启用线程安全。SQLITE_THREADSAFE编译标志控制 SQLite 中是否包含代码以使其能够在多线程环境中安全运行。默认值为SQLITE_THREADSAFE=1。这意味着序列化模式。在这种模式下:

在这种模式下(这是 SQLite 使用 SQLITE_THREADSAFE=1 编译时的默认设置),SQLite 库本身将序列化对数据库连接和准备语句的访问,以便应用程序可以自由地在不同线程中使用相同的数据库连接或相同的准备语句同时。

使用sqlite3_threadsafe()函数检查 Sqlite 库SQLITE_THREADSAFE编译标志。

默认库线程安全行为可以通过sqlite3_config(). 使用SQLITE_OPEN_NOMUTEXSQLITE_OPEN_FULLMUTEX标志 atsqlite3_open_v2()来调整单个数据库连接的线程模式。