在多线程中使用SQLite数据库
在多线程中同时访问 SQLite 数据库,使用 sqlite3_step
,sqlite3_prepare
和其他的函数可能引发 SQLITE_BUSY
或 SQLITE_LOCKED
错误。SQLITE_BUSY
通常意味着 sqlite
需要一个锁,但是一定时间内无法获得。 SQLITE_BUSY
和 SQLITE_LOCKED
最大的区别是:
- SQLITE_LOCKED: if you get this from a
sqlite3_step
statement, you MUST callsqlite3_reset
on the statement handle. You should only get this on the first call tosqlite3_step
, so once reset is called you can actually "retry" yoursqlite3_step
call. On other operations, it's the same asSQLITE_BUSY
- SQLITE_BUSY : There is no need to call s
qlite3_reset
, just retry your operation after waiting a bit for the lock to be released.
在多线程中使用 SQLite 数据的方法和注意事项:
- 确保sqlite使用多线程标记
-DTHREADSAFE=1
编译。 - 确保在每个线程中分别使用各自的连接打开 SQLite,不要在线程间共享连接。
尽可能处理当一个或多个线程同时访问同一数据库文件时造成的冲突:妥善处理
SQLITE_BUSY
。while (continueTrying) { retval = sqlite_exec(db, sqlQuery, callback, 0, &msg); switch (retval) { case SQLITE_BUSY: Log("[%s] SQLITE_BUSY: sleeping fow a while...", threadName); sleep a bit... (use something like sleep(), for example) break; case SQLITE_OK: continueTrying = NO; // We're done break; default: Log("[%s] Can't execute \"%s\": %s\n", threadName, sqlQuery, msg); continueTrying = NO; break; } } return retval;
- 在使用
INSERT
,UPDATE
,DELETE
等语句时使用事物(transactions )机制。 - SQLite 是非常保守的线程模型,当你做一个写操作,包括开始事务做一个
INSERT/UPDATE/DELETE
,其他线程将被阻塞,直到该操作完成。 - 如果你不使用一个事务,那么事务是隐式的,所以如果你开始
INSERT/DELETE/UPDATE
,sqlite将尝试获得独占锁定,并且在释放锁之前完成操作。 - 如果你使用
BEGIN EXCLUSIVE
语句,在事物操作开始之前它将获得一个独占锁,COMMIT
或ROLLBACK
语句会释放这个锁。
参考文章:
http://www.sqlite.org/cvstrac/wiki?p=MultiThreading
http://stackoverflow.com/questions/1680249/how-to-use-sqlite-in-a-multi-threaded-application