我在多线程应用程序中使用sqlite3
(它使用SQLITE_THREADSAFE=2
编译)。在观察窗口,我看到sqlite->busyTimeout == 600000
,我。即应该有10分钟超时。但是,sqlite3_step
以比10分钟后更快的速度返回SQLITE_BUSY
(实际上会立即返回,就像我从未调用sqlite3_busy_timeout
一样)。
sqlite3
忽略超时并立即返回错误的原因是什么?
答案 0 :(得分:6)
一种可能性:SQLite在检测到死锁时忽略超时。
方案如下。事务A
以读者身份开始,后来尝试执行写入。交易B
是一个作家(或者以这种方式开始,或者作为读者开始并首先提升为作家)。 B
持有RESERVED
锁定,等待读者清除,以便开始编写。 A
持有SHARED
锁定(它是读者)并尝试获取RESERVED
锁定(因此它可以开始写入)。有关各种锁类型的说明,请参阅http://sqlite.org/lockingv3.html
在这种情况下取得进展的唯一方法是回滚其中一个事务。没有多少等待会有所帮助,因此当SQLite检测到这种情况时,它不会遵循忙碌超时。
有两种方法可以避免死锁:
BEGIN IMMEDIATE
启动最终可能需要编写的事务 - 这样,它就会立即以编写者身份启动。这当然会降低系统中的潜在并发性,作为避免死锁的代价。答案 1 :(得分:-1)
我做了很多测试,并在这里与其他在多线程环境中使用SQLite
的人分享。 SQLite
线程支持没有详细记录,没有任何好的教程可以在一个地方描述所有线程问题。我制作了一个测试程序,创建了100个线程,并将随机查询(INSERT
,SELECT
,UPDATE
,DELETE
)同时发送到单个数据库。我的回答是基于这个程序观察。
唯一真正的线程安全日志模式是WAL
。它允许多个连接以与单线程应用程序相同的方式在一个进程内为同一数据库执行所需的任何操作。任何其他模式都不是独立于超时,繁忙处理程序和SQLITE_THREADSAFE
预处理程序定义的线程安全。它们会定期生成SQLITE_BUSY
,看起来像复杂的编程任务总是期望这样的错误并始终处理它。如果你需要线程安全的SQLite
永远不会像signle线程一样返回SQLITE_BUSY
,你必须设置WAL
日志模式。
此外,您必须设置SQLITE_THREADSAFE=2
或SQLITE_THREADSAFE=1
预处理器定义。
完成后,您必须从2个选项中进行选择:
sqlite3_busy_timeout
。这就足够了,你不需要打电话给sqlite3_busy_handler
,即使是文件也不明显。它为您提供“默认”,“内置”超时功能。sqlite3_busy_handler
并自行实施超时。我不明白为什么,但可能需要一些非标准的操作系统。当您调用sqlite3_busy_handler
时,它会将超时重置为0(即禁用)。对于桌面Linux
& Windows
你不需要它,除非你想编写更复杂的代码。