小巧、快速、可靠。
三选一。

SQLite C 接口

解锁通知

int sqlite3_unlock_notify(
  sqlite3 *pBlocked,                          /* Waiting connection */
  void (*xNotify)(void **apArg, int nArg),    /* Callback function to invoke */
  void *pNotifyArg                            /* Argument to pass to xNotify */
);

在共享缓存模式下运行时,如果无法获得共享缓存或共享缓存中单个表的所需锁,则数据库操作可能会失败并出现SQLITE_LOCKED错误。有关共享缓存锁定的说明,请参见SQLite 共享缓存模式。此 API 可用于注册一个回调,当当前持有所需锁的连接释放该锁时,SQLite 将调用该回调。仅当库在编译时定义了SQLITE_ENABLE_UNLOCK_NOTIFY C 预处理器符号时,此 API 才可用。

另请参阅:使用 SQLite 解锁通知功能

当数据库连接完成其当前事务(通过提交或回滚)时,将释放共享缓存锁。

当连接(称为阻塞连接)无法获得共享缓存锁并向调用方返回 SQLITE_LOCKED 时,内部会存储已锁定所需资源的数据库连接(阻塞连接)的身份。应用程序在收到 SQLITE_LOCKED 错误后,可以调用 sqlite3_unlock_notify() 方法,并将阻塞连接句柄作为第一个参数,以注册一个回调,该回调将在阻塞连接的当前事务结束时被调用。回调是在结束阻塞连接事务的sqlite3_stepsqlite3_close调用中调用的。

如果在多线程应用程序中调用 sqlite3_unlock_notify(),则在调用 sqlite3_unlock_notify() 时,阻塞连接可能已经完成了其事务。如果发生这种情况,则指定的回调将立即从 sqlite3_unlock_notify() 的调用中调用。

如果阻塞连接正在尝试获取共享缓存表的写锁,并且多个其他连接当前持有同一表的读锁,则 SQLite 会任意选择其中一个连接用作阻塞连接。

阻塞连接最多只能注册一个解锁通知回调。如果在阻塞连接已经注册了解锁通知回调时调用 sqlite3_unlock_notify(),则新回调将替换旧回调。如果使用 NULL 指针作为第二个参数调用 sqlite3_unlock_notify(),则任何现有的解锁通知回调都将被取消。还可以通过使用sqlite3_close()关闭阻塞连接来取消阻塞连接的解锁通知回调。

解锁通知回调不可重入。如果应用程序从解锁通知回调中调用任何 sqlite3_xxx API 函数,则可能会导致崩溃或死锁。

除非检测到死锁(见下文),否则 sqlite3_unlock_notify() 始终返回 SQLITE_OK。

回调调用细节

注册解锁通知回调时,应用程序提供一个单一的 void* 指针,在调用回调时将其传递给回调。但是,回调函数的签名允许 SQLite 将一个 void* 上下文指针数组传递给它。传递给解锁通知回调的第一个参数是指向 void* 指针数组的指针,第二个参数是数组中的条目数。

当阻塞连接的事务结束时,可能有多个已注册解锁通知回调的阻塞连接。如果两个或多个此类阻塞连接指定了相同的回调函数,则不会多次调用回调函数,而是会调用一次,并将阻塞连接指定的 void* 上下文指针集捆绑到一个数组中。这使应用程序有机会优先处理与一组已解锁数据库连接相关的任何操作。

死锁检测

假设在注册解锁通知回调后,数据库在采取任何进一步操作之前会等待回调发出(这是一个合理的假设),那么使用此 API 可能会导致应用程序发生死锁。例如,如果连接 X 正在等待连接 Y 的事务结束,并且类似地,连接 Y 正在等待连接 X 的事务,那么这两个连接都不会继续,并且系统可能会无限期地保持死锁状态。

为了避免这种情况,sqlite3_unlock_notify() 执行死锁检测。如果对 sqlite3_unlock_notify() 的特定调用会导致系统处于死锁状态,则会返回 SQLITE_LOCKED,并且不会注册任何解锁通知回调。如果连接 A 已注册在连接 B 的事务结束时进行解锁通知回调,并且连接 B 本身已注册在连接 A 的事务结束时进行解锁通知回调,则系统被认为处于死锁状态。还检测间接死锁,因此如果连接 B 已注册在连接 C 的事务结束时进行解锁通知回调,其中连接 C 正在等待连接 A,则系统也被认为处于死锁状态。允许任意数量的间接级别。

"DROP TABLE" 异常

当对sqlite3_step()的调用返回 SQLITE_LOCKED 时,几乎总是适合调用 sqlite3_unlock_notify()。但是,有一个例外。执行 "DROP TABLE" 或 "DROP INDEX" 语句时,SQLite 会检查是否存在属于同一连接的任何当前正在执行的 SELECT 语句。如果存在,则返回 SQLITE_LOCKED。在这种情况下,不存在“阻塞连接”,因此调用 sqlite3_unlock_notify() 会导致立即调用解锁通知回调。如果应用程序随后重新尝试 "DROP TABLE" 或 "DROP INDEX" 查询,则可能会导致无限循环。

解决此问题的一种方法是检查 sqlite3_step() 调用返回的扩展错误代码。如果存在阻塞连接,则扩展错误代码将设置为 SQLITE_LOCKED_SHAREDCACHE。否则,在特殊的 "DROP TABLE/INDEX" 情况下,扩展错误代码仅为 SQLITE_LOCKED。

另请参见对象常量函数列表。