sqlite3.dylib:非法多线程访问数据库连接

时间:2018-03-09 17:11:45

标签: ios sqlite

我有一个使用sqlite3的iOS应用程序,我遇到多线程问题,导致应用程序崩溃illegal multi-threaded access to database connection。当然,这是因为我正在使用多线程;问题是,我的sqlyte3实例配置为使用多线程:

sqlite3_config(SQLITE_CONFIG_MULTITHREAD);

即使我使用多线程(sqlite3 build也使用多线程标记编译),当多个线程同时写入或读取数据库时,它会导致我的应用程序崩溃。

崩溃报告

Application Specific Information:
BUG IN CLIENT OF sqlite3.dylib: illegal multi-threaded access to database connection

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x00000001823ed2fc
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [0]
Triggered by Thread:  12

Thread 12 Crashed:

0   libsqlite3.dylib                0x00000001823ed2fc sqlite3MutexMisuseAssert + 144 (sqlite3.c:23788)
1   libsqlite3.dylib                0x00000001823ed2ec sqlite3MutexMisuseAssert + 128 (once.h:84)
2   libsqlite3.dylib                0x000000018235248c sqlite3LockAndPrepare + 320 (sqlite3.c:23801)
3   MyCodeCall.m ...........

我一直在努力解决这个问题,我在谷歌上找不到任何关于这个问题的不幸。

更新

+(sqlite3*) getInstance {
  if (instance == NULL) {
    sqlite3_shutdown();
    sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
    sqlite3_initialize();

    NSLog(@"isThreadSafe %d", sqlite3_threadsafe());

    const char *path = [@"./path/to/db/db.sqlite" cStringUsingEncoding:NSUTF8StringEncoding];

    if (sqlite3_open_v2(path, &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL) != SQLITE_OK) {
      NSLog(@"Database opening failed!");
    }
  }

  return instance;
}

4 个答案:

答案 0 :(得分:12)

事实证明SQLITE_CONFIG_MULTITHREAD模式在多线程环境中运行良好,只要您不同时使用相同的连接;这恰好是我的确切场景。因此,要解决此问题,您可以为每个线程打开一个新连接,或者使用SQLITE_CONFIG_SERIALIZED标志以完全互斥模式使用SQLITE_OPEN_FULLMUTEX来打开连接。

辅助方法最终如下:

+(sqlite3*) getInstance {
  if (instance == NULL) {
    sqlite3_shutdown();
    sqlite3_config(SQLITE_CONFIG_SERIALIZED);
    sqlite3_initialize();

    NSLog(@"isThreadSafe %d", sqlite3_threadsafe());

    const char *path = [@"./path/to/db/db.sqlite" cStringUsingEncoding:NSUTF8StringEncoding];

    if (sqlite3_open_v2(path, &database, SQLITE_OPEN_READWRITE|SQLITE_OPEN_FULLMUTEX, NULL) != SQLITE_OK) {
      NSLog(@"Database opening failed!");
    }
  }

  return instance;
}

答案 1 :(得分:1)

请使用SQLite共享缓存模式 https://www.sqlite.org/sharedcache.html

sqlite3_open_v2(path.path,&db,SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE,nil)!= SQLITE_OK

SQLITE_OPEN_SHAREDCACHE

可靠100%:)

答案 2 :(得分:0)

如果有人在Swift中遇到此问题。解决方案将是:

let dbName = "first.db"
static let shared = DatabaseManger()
var db: OpaquePointer?
let SQLITE_TRANSIENT = unsafeBitCast(-1, to: sqlite3_destructor_type.self)

private init(){
    print("singletone initialized")
    sqlite3_shutdown();
    let dbPath = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
        .appendingPathComponent(dbName)
    if sqlite3_open_v2(dbPath.path, &db, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX, nil) == SQLITE_OK {
        print("Successfully opened database connection at \(dbPath.path)")
    }
    else {
        print("unable to open database connection")
    }
}

此代码已在4.0和4.2版中快速测试

答案 3 :(得分:0)

来自https://www.sqlite.org/threadsafe.html

  

SQLite支持三种不同的线程模式:

     

单线程。在这种模式下,所有互斥锁都被禁用,SQLite被禁用。   一次不能在多个线程中使用是不安全的。

     

多线程。在这种模式下,SQLite可以安全地被多个用户使用   线程,前提是不使用单个数据库连接   同时在两个或多个线程中。

     

序列化。在序列化模式下,SQLite可以安全地被多个用户使用   线程无限制。

在iOS中,SQLite的默认线程模式为SQLITE_OPEN_NOMUTEX(等于多线程),当多个线程同时使用一个连接写入或读取数据库时,这是不安全的。 将线程模式更改为序列化可能对您有所帮助。您可以使用sqlite3_config()sqlite3_open_v2()更改线程模式。