【发布时间】:2015-01-09 00:10:42
【问题描述】:
我有一个 OS X 应用程序(Yosemite,10.10),它执行长时间运行的作业,涉及跨多个线程大量使用 sqlite。而且我在 8 个线程中遇到了死锁,所有这些都被困在连接到不同数据库文件的 sqlite 代码中。它们之间没有明显的资源相关联系。我正在新的 Mac Pro 上调试它(2013 年末)。
其中四个在此堆栈中。其中,三个在同一张表上操作(同样,不同的数据库文件);三个在更新,一个在查询。
__psynch_mutexwait
_pthread_mutex_lock
unixLock
sqlite3PagerSharedLock
sqlite3BtreeBeginTrans
sqlite3VdbeExec
sqlite3_step
一个在这个堆栈中,更新与上面堆栈中的三个相同的表。
guarded_close_np
nolockClose
pager_end_transaction
sqlite3BtreeCommitPhaseTwo
sqlite3VdbeHalt
sqlite3VdbeExec
sqlite3_step
两个在这个堆栈中,在不同位置打开同名的数据库文件。打开方式为SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX。
__psynch_mutexwait
_pthread_mutex_lock
sqlite3ParseUri
openDatabase
一个在这个堆栈上,正在结束一个事务。
__psynch_mutexwait
_pthread_mutex_lock
unixLock
sqlite3VdbeHalt
sqlite3VdbeExec
sqlite3_step
那么,问题来了:什么会导致 sqlite 在不涉及任何共享资源的情况下死锁?
更新:我现在有七个线程被锁定,调用 sqlite3_open_v2 和一个在 sqlite3_close,都在不同的数据库文件上(几个同名,但在不同的文件夹中)。堆栈是:
__psynch_mutexwait
_pthread_mutex_lock
sqlite3ParseUri
openDatabase
关闭堆栈是:
guarded_close_np
unixClose
sqlite3PagerClose
sqlite3BtreeClose
sqlite3LeaveMutexAndCloseZombie
sqlite3Close
通过修复一些内存泄漏(这不适用于 ARC)并删除一些事务语句,我能够让它在锁定之前运行更长时间。
更新 2: 我已经通过 sqlite3_config (documentation) 连接了 SQLITE_LOG,我看到相当多的代码 28 (sqlite_warning) 与消息“文件在打开时重命名:”。
更新 3: 我擦拭了机器并重新安装了 Yosemite,以排除文件系统问题。我仍然以同样的方式锁定。它会运行几分钟,然后线程一一锁定。在guarded_close_np 有一个,卡在指令jae <address here> 的汇编中,跳转到的地址有指令retq。我已经向separate question 询问了有关重命名文件的 sqlite 日志消息,希望它是相关的。
【问题讨论】:
-
我怀疑你弄错了,有些人正在访问同一个文件。但请注意,SQLite 在某些配置中会创建一个“日志”(WAL)文件,即使 SQLite 文件不同,它们也有可能尝试使用相同的日志文件。
-
不,我只是再次检查了所有线程并检查了所有传递给 sqlite 函数的路径。如果确实有多个线程访问同一个文件,那么它会以一种非常非常奇怪的方式发生。并且 DB 文件是使用 WAL 日志创建的,然后转换为 DELETE(它们将来只会用作只读)。
-
我不是很喜欢 OSx 的东西,但是有没有工具可以显示进程中打开的文件?
-
Yes。我看到很多 sqlite-shm 文件句柄打开,但没有一个与被锁定的特定文件完全对应。 SQLite docs 声明在删除 wal 文件时应该删除 -shm 文件,但在我的情况下不会发生这种情况。当我转换为 DELETE (
PRAGMA journal_mode=DELETE) 时,wal 文件消失但 shm 文件保留。 -
我对此没有答案,但我想注册一个“我也是” - 我看到三个线程非常相似,三个线程都卡在不同数据库上的
sqlite3_step中。其中一个线程在guarded_close_np,其他线程在__psynch_mutexwait。
标签: multithreading macos sqlite deadlock