我有一个 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: 我已连接 SQLITE_LOG
通过sqlite3_config
( documentation ),并且我看到大量代码 28 ( sqlite_warning ) 的日志记录,并带有消息“文件在打开时重命名:”。
更新 3: 我删除了机器并重新安装了 Yosemite,试图排除文件系统问题。我仍然以同样的方式锁定。它会运行几分钟,然后线程一个接一个地锁定。 guarded_close_np
有一个,陷入汇编指令jae <address here>
跳转到的地址有指令 retq
。我问过separate question关于文件被重命名的 sqlite 日志消息,希望与其相关。
最佳答案
听起来您陷入了 UNIX 主互斥体的困境,需要在文件关闭之前获取该互斥体:
/*
** Close a file.
*/
static int unixClose(sqlite3_file *id){
int rc = SQLITE_OK;
unixFile *pFile = (unixFile *)id;
verifyDbFile(pFile);
unixUnlock(id, NO_LOCK);
unixEnterMutex(); <- HERE
...
此互斥锁主要在低级文件操作期间保留。您必须找到保存互斥锁的线程并查看它在做什么。也许它在缓慢或损坏的文件系统上运行。
关于multithreading - 具有不同文件的多个线程之间的 Sqlite 死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27784704/