【问题标题】:sqlite3.dylib: illegal multi-threaded access to database connectionsqlite3.dylib:非法多线程访问数据库连接
【发布时间】:2018-08-18 07:47:26
【问题描述】:

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

sqlite3_config(SQLITE_CONFIG_MULTITHREAD);

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

崩溃报告

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;
}

【问题讨论】:

  • 您是否使用SQLITE_OPEN_NOMUTEX 标志作为sqlite3_open_v2 调用的一部分打开了数据库?
  • @rmaddy 不,我正在使用 sqlite3 api sqlite3_open
  • 你真的应该使用sqlite3_open_v2。始终使用较新的 API。
  • @rmaddy 我已更改为sqlite3_open_v2,但仍然没有区别,相同的异常导致应用程序崩溃。我已经用 db helper 方法更新了问题。
  • 所以将| SQLITE_OPEN_NOMUTEX 添加到其他打开的标志没有帮助?

标签: ios sqlite


【解决方案1】:

事实证明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;
}

【讨论】:

  • 对我来说不存在sqlite3_config,我也有同样的问题。
  • @Augusto 它对我来说也不存在,但只是添加 SQLITE_OPEN_FULLMUTEX 似乎可以解决这个问题。
【解决方案2】:

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

let dbName = "first.db"
static let shared = DatabaseManger()
var db: OpaquePointer?


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")
    }
}

此代码在 swift 版本 4.0 和 4.2 中测试

【讨论】:

  • SQLITE_TRANSIENT有什么用?
  • @AppDeveloper,SQLITE_TRANSIENT 与这个 init 方法无关,需要插入一些 blob 值,从答案中删除
【解决方案3】:

请使用 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% 可靠 :)

【讨论】:

    【解决方案4】:

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

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

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

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

    序列化。在序列化模式下,SQLite 可以被多人安全使用 线程没有限制。

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

    【讨论】:

      猜你喜欢
      • 2018-12-11
      • 2014-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-17
      • 2018-02-21
      相关资源
      最近更新 更多