我有一个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
(documentation)与sqlite3_config
联系,我看到了大量的日志记录代码28(sqlite_warning)与消息"文件在打开时重命名:"。
更新3:我擦除了机器并重新安装了Yosemite以试图排除文件系统问题。我仍然以同样的方式锁定。它将运行几分钟,然后一个接一个地将线程锁定。在guarded_close_np
处有一个,在指令jae <address here>
处被装配,其中跳转到的地址具有指令retq
。我已经向separate question询问了有关重命名文件的sqlite日志消息,希望它与之相关。
答案 0 :(得分:2)
听起来好像你被卡在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
...
此互斥锁主要在低级文件操作期间保留。你必须找到包含互斥锁的线程,看看它在做什么。也许它在一个缓慢或破碎的文件系统上运行。
答案 1 :(得分:1)
我的应用是通过链接动态库来使用sqlite。我从here下载了最新的SQLite源合并(编写时为3.8.7.4)并将其编译到应用程序中,一切都开始工作了。所以也许这是3.8.5中的一个错误。显然,直接将源代码编译到应用程序中的是recommended way,无论如何都要使用sqlite。
我仍然不知道究竟是什么导致了这个问题。我唯一能想到的是它与我如何创建数据库文件有关:我正在使用NSFileManager createFileAtPath
创建一个空文件,然后使用{{1}将其传递给sqlite3_open_v2
}作为flags参数的一部分。因此,它将数据库写入现有文件,而不是在指定位置创建数据库文件。