代码之家  ›  专栏  ›  技术社区  ›  Edan Maor

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

  •  40
  • Edan Maor  · 技术社区  · 15 年前

    我正在用开发应用程序 SQLite 作为数据库,我很难理解如何在多个线程中使用它(不幸的是,其他堆栈溢出问题都没有真正帮助过我)。

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

    我从每个线程使用不同的事务,即在每个线程的更新周期开始时,我都有一个开始。实际上,每个线程实际执行的操作是调用“begin”,从数据库中删除它需要“update”的所有行,然后用新值再次插入它们(这是在我的应用程序上下文中必须执行的操作)。

    现在,我正试图理解我是如何着手实施这一计划的。我试过四处阅读(关于堆栈溢出的其他答案,sqlite站点),但我没有找到所有答案。我想知道的是:

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

    如果有人能回答这些问题/给我指出一个好资源的方向,我将非常感激。

    更新1: 从我迄今为止所读到的所有内容来看,似乎无论如何都不能有两个线程写入数据库文件。

    见: http://www.sqlite.org/lockingv3.html .在3.0节中:保留锁意味着进程计划在将来某个时候写入数据库文件,但它当前只是从文件中读取。一次只能激活一个保留锁,尽管多个共享锁可以与一个保留锁共存。

    这是否意味着我可以只生成一个线程来每次更新一组行?例如,有某种类型的轮询线程,它决定我需要更新一些行,然后创建一个新的线程来执行此操作,但一次不能超过一个?因为它看起来像我创建的任何其他线程,所以在第一个线程完成之前,sqlite都会很忙。

    我理解的正确吗?

    顺便说一句,谢谢你的回答,他们帮了很多忙。

    6 回复  |  直到 8 年前
        1
  •  22
  •   dalle    12 年前

    退房 this link . 最简单的方法是自己锁定,避免在线程之间共享连接。可以找到另一个好资源 here 最后得出结论:

    1. 确保使用-dthreasafe=1编译sqlite。

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

    3. 确保处理一个或多个线程在同时访问DB文件时可能发生冲突的可能性:适当地处理sqlite_busy。

    4. 确保在事务中包含修改数据库文件的命令,如insert、update、delete和其他命令。

        2
  •  27
  •   Snazzer    15 年前

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

    1. 确保使用多线程标志编译sqlite。
    2. 必须对sqlite文件调用open才能在每个线程上创建连接,而不在线程之间共享连接。
    3. sqlite有一个非常保守的线程模型,当您执行写操作时,其中包括将要执行插入/更新/删除操作的打开事务,其他线程将被阻塞,直到该操作完成。
    4. 如果不使用事务,则事务是隐式的,因此如果启动插入/删除/更新,则sqlite将尝试获取独占锁,并在释放前完成该操作。
    5. 如果您执行begin exclusive语句,它将在该事务中执行操作之前获取一个exclusive锁。提交或回滚将释放锁。
    6. 您的sqlite3_步骤、sqlite3_准备和其他一些调用可能会返回sqlite_busy或sqlite_locked。sqlite_busy通常意味着sqlite需要获取锁。两个返回值之间的最大差异:
      • sqlite_locked:如果您从sqlite3_step语句中获取此信息,则必须在语句句柄上调用sqlite3_reset。您应该只在第一次调用sqlite3_步骤时得到它,所以一旦调用了reset,您就可以“重试”sqlite3_步骤调用。在其他操作中,它与sqlite_busy相同
      • sqlite_busy:不需要调用sqlite3_reset,只需在等待释放锁后重试操作。
        3
  •  9
  •   adechiaro    12 年前

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

        4
  •  3
  •   Macarse    15 年前

    Check this code from the SQLite wiki .

    我用C做了类似的事情,我上传了代码 here .

    希望它有用。

        5
  •  1
  •   Nikita    8 年前

    现代版本的sqlite默认启用了线程安全。 SQLITE_THREADSAFE 编译标志控制代码是否包含在sqlite中,以使其能够在多线程环境中安全运行。 违约 价值是 SQLITE_THREADSAFE=1 . 它意味着 Serialized mode . 在这种模式下:

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

    使用 sqlite3_threadsafe() 用于检查sqlite库的函数 sqlite_threasafe公司 编译标志。

    可以通过以下方式更改默认库线程安全行为: sqlite3_config() . 使用 SQLITE_OPEN_NOMUTEX SQLITE_OPEN_FULLMUTEX 旗帜在 sqlite3_open_v2() 调整单个数据库连接的线程模式。

        6
  •  0
  •   seh Alexei    8 年前

    总结

    SQLite中的事务是可序列化的。

    在提交之前,一个数据库连接中所做的更改对所有其他数据库连接都不可见。

    查询可以查看在查询开始之前在同一数据库连接上完成的所有更改,而不管这些更改是否已提交。

    如果在查询开始运行之后但在查询完成之前,同一数据库连接上发生了更改,那么查询是否会看到这些更改将是未定义的。

    如果在查询开始运行后但在查询完成前对同一数据库连接发生更改,则查询可能会多次返回更改的行,或者返回以前删除的行。

    对于前四项,两个数据库连接使用相同的共享缓存,并启用pragma read_uncommitted 被认为是相同的数据库连接,而不是单独的数据库连接。


    除了上面关于多线程访问的信息之外,还值得一看 this page on isolation ,自从这个原始问题和预写日志的引入之后,许多事情都发生了变化。( 沃尔 )

    这似乎是一种混合的方法,将多个连接打开到数据库,提供了足够的并发性保证,以允许多线程写入事务的好处换取打开新连接的费用。