在多线程中同时访问 SQLite 数据库,使用 sqlite3_stepsqlite3_prepare 和其他的函数可能引发 SQLITE_BUSYSQLITE_LOCKED 错误。SQLITE_BUSY 通常意味着 sqlite 需要一个锁,但是一定时间内无法获得。 SQLITE_BUSYSQLITE_LOCKED 最大的区别是:

  • SQLITE_LOCKED: if you get this from a sqlite3_step statement, you MUST call sqlite3_reset on the statement handle. You should only get this on the first call to sqlite3_step, so once reset is called you can actually "retry" your sqlite3_step call. On other operations, it's the same as SQLITE_BUSY
  • SQLITE_BUSY : There is no need to call sqlite3_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;
  • 在使用INSERTUPDATEDELETE 等语句时使用事物(transactions )机制。
  • SQLite 是非常保守的线程模型,当你做一个写操作,包括开始事务做一个 INSERT/UPDATE/DELETE,其他线程将被阻塞,直到该操作完成。
  • 如果你不使用一个事务,那么事务是隐式的,所以如果你开始 INSERT/DELETE/UPDATE,sqlite将尝试获得独占锁定,并且在释放锁之前完成操作。
  • 如果你使用 BEGIN EXCLUSIVE 语句,在事物操作开始之前它将获得一个独占锁,COMMITROLLBACK语句会释放这个锁。

参考文章:
http://www.sqlite.org/cvstrac/wiki?p=MultiThreading
http://stackoverflow.com/questions/1680249/how-to-use-sqlite-in-a-multi-threaded-application

标签: SQLite, 多线程, 死锁, SQLITE_BUSY, SQLITE_LOCKED

添加新评论