我有一个多线程应用程序,它使用 SQLite DB 进行持久存储。 该应用程序使用一个持久连接。 它在启动时和每次回滚后准备语句。它还使用
journal_model=WAL
来帮助并发访问。
我发现当我的
listUsers()
函数被同时调用时,有时结果似乎会在调用之间泄漏。 我知道有 100 个用户,但有时结果是 100+,我认为这是因为一个线程上的 sqlite3_step
正在从本来可以在另一个线程上调用的 sqlite3_step
中提取结果。
这可能吗?
另外,我尝试将
listUsers()
包装在事务中,但有时它会抛出错误,表明一个事务正在另一个事务中启动。 同样,我认为这是因为两个线程共享同一个数据库连接。
这个问题有通用的解决方案吗? 每个线程一个连接? 我不太关心偶尔锁定的数据库或长时间的等待,这只是令人担忧的无效结果。
如果有帮助,我的
listUsers()
看起来像
std::vector<User> listUsers()
{
char* err = 0;
auto users = std::vector<User>();
sqlite3_reset(list_user_stmt);
beginTransaction();
while (SQLITE_ROW == sqlite3_step(list_user_stmt))
{
auto id = sqlite3_column_int(list_user_stmt, 0);
auto name = sqlite3_column_text(list_user_stmt, 1);
users.push_back(User{id, name});
}
commitTransaction();
return users;
}
您不能同时从多个线程使用同一个连接。 SQLite 数据库对于并发访问是安全的,但单个连接则不然(据我所知,它将是“线程安全”,因为它实际上不会损坏任何数据,但它的行为不会像你一样)在这种情况下,我们希望 – 特别是单个连接之间没有事务隔离,因为这实际上没有任何意义)。如果您需要同时从多个线程访问该线程,您可以在连接上放置一个互斥体,并且在任何时候只有一个线程访问数据库(通常 SQLite 不需要,但如果例如您缓存在应用程序级别的一些结果中,您可能需要这样做以保持缓存的视图一致),每个线程都有一个连接,或者如果您有很多线程,则使用连接池作为合理的中间立场。