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。

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