如何在multithreading应用程序中使用SQLite?
我正在用SQLite开发一个应用程序作为数据库,并且在理解如何在multithreading中使用它时遇到了一些麻烦(不幸的是,其他堆栈溢出问题都没有帮助我)。
我的用例:数据库有一个表,我们称它为“A”,它具有不同的行组(基于它们的一列)。 我有从表A读取内容的应用程序的“主线程”。此外,我偶尔决定更新某一组行。 要做到这一点,我想产卵一个新的线程,删除组的所有行,并重新插入它们(这是在我的应用程序的上下文中执行它的唯一方法)。 这可能发生在同一时间不同的群体,所以我可能有2+线程试图更新数据库。
我从每个线程使用不同的事务,IE在每个线程的更新周期开始,我有一个开始。 实际上,每个线程实际上所做的就是调用“BEGIN”,从数据库中删除所有需要“更新”的行,并用新值插入它们(这是在我的上下文中必须完成的方式应用)。
现在,我正试图了解如何执行此操作。 我试过阅读(堆栈溢出,SQLite网站上的其他答案),但我还没有find所有的答案。 以下是我想知道的一些事情:
- 我是否需要调用“open”并从每个线程创build一个新的sqlite结构?
- 我是否需要为所有这些添加任何特殊的代码,或者它足以产生不同的线程,更新行,这很好(因为我使用不同的交易)?
- 我看到了一些关于不同锁types的事情,以及我可能会通过调用某些API来获得“SQLite繁忙”的事实,但老实说,当我需要考虑所有这些时,我没有看到任何完整的解释。 我需要吗?
如果有人能够回答问题/指出我的方向是一个好的资源,我会非常感激。
更新1:从目前为止我读过的所有东西,似乎不能有两个线程谁将要写入数据库文件。
请参阅: http : //www.sqlite.org/lockingv3.html 。 在第3.0节中:保留锁意味着进程正在计划在将来的某个时刻写入数据库文件,但是它目前只是从文件中读取数据。 尽pipe多个SHARED锁可以与一个RESERVED锁共存,但一次只能有一个RESERVED锁。
这是否意味着我可能只会产生一个线程来每次更新一组行? 也就是说,有一些轮询线程决定我需要更新一些行,然后创build一个新的线程来做到这一点,但一次只能有一个? 因为它看起来像我创build的任何其他线程将只是得到SQLITE_BUSY,直到第一个线程完成,无论如何。
我的理解是否正确?
顺便说一句,感谢迄今为止的答案,他们已经帮了很多。
看看这个链接 。 最简单的方法是自己locking,并避免共享线程之间的连接。 另一个很好的资源可以在这里find,最后是:
-
确保你正在用-DTHREADSAFE = 1编译SQLite。
-
确保每个线程打开数据库文件并保持自己的sqlite结构。
-
确保在处理同时访问db文件时一个或多个线程发生冲突的可能性:适当地处理SQLITE_BUSY。
-
确保在事务中包含修改数据库文件的命令,如INSERT,UPDATE,DELETE等。
一些开始使用SQLlite进行multithreading的步骤:
- 确保sqlite是用multithreading标志编译的。
- 你必须在你的sqlite文件上调用open来在每个线程上创build一个连接,不要共享线程之间的连接。
- SQLite有一个非常保守的线程模型,当你做一个写入操作时,包括打开正在执行INSERT / UPDATE / DELETE的事务,其他线程将被阻塞,直到这个操作完成。
- 如果你不使用一个事务,那么事务是隐式的,所以如果你启动了一个INSERT / DELETE / UPDATE,sqlite会尝试获得一个排它锁,并在释放它之前完成操作。
- 如果您执行BEGIN EXCLUSIVE语句,则在执行该事务中的操作之前,它将获得排它锁。 COMMIT或ROLLBACK将释放该锁。
- 您的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,只需等待一会儿释放锁就重试你的操作。
我意识到这是一个古老的线程,答案是好的,但我最近一直在研究这个问题,并且对一些不同的实现进行了有趣的分析。 主要涉及连接共享,消息传递,线程本地连接和连接池的优点和缺点。 看看这里: http : //dev.yorhel.nl/doc/sqlaccess
从SQLite维基检查这个代码 。
我已经做了类似的C和我上传的代码在这里 。
我希望这是有用的。
现代版本的SQLite默认启用线程安全。 SQLITE_THREADSAFE
编译标志控制SQLite中是否包含代码以使其能够在multithreading环境中安全地运行。 默认值是SQLITE_THREADSAFE=1
。 这意味着序列化模式 。 在这种模式下:
在这种模式下(当SQLite使用SQLITE_THREADSAFE = 1进行编译时,默认情况下),SQLite库本身将对数据库连接和预处理语句的访问进行序列化,以便应用程序可以自由地在不同线程中使用相同的数据库连接或相同的预处理语句与此同时。
使用sqlite3_threadsafe()
函数来检查Sqlite库SQLITE_THREADSAFE
编译标志。
默认库的线程安全行为可以通过sqlite3_config()
来改变。 在sqlite3_open_v2()
上使用SQLITE_OPEN_NOMUTEX
和SQLITE_OPEN_FULLMUTEX
标志来调整各个数据库连接的线程模式。
概要
SQLite中的事务是SERIALIZABLE的。
在提交之前,在一个数据库连接中进行的更改对所有其他数据库连接是不可见的。
查询查看在查询开始之前在同一个数据库连接上完成的所有更改,而不pipe这些更改是否已经提交。
如果在查询开始运行之后但在查询完成之前在同一数据库连接上发生更改,则不确定查询是否会看到这些更改。
如果在查询开始运行之后但在查询完成之前在同一数据库连接上发生更改,则查询可能会多次返回一个已更改的行,或者可能返回以前删除的行。
就前四项而言,两个使用相同共享高速caching并启用PRAGMA read_uncommitted
的数据库连接被认为是相同的数据库连接,而不是单独的数据库连接。
除了以上有关multithreading访问的信息之外,可能值得关注隔离这个页面 ,因为自从这个原始问题以及提前写入日志( WAL )的引入以来,许多事情已经发生了变化。
似乎有一个混合的方法,有多个连接对数据库开放,提供了足够的并发性保证,从而允许开放一个新的连接,并允许multithreading写入事务。