在iOS 4.0上使用SQLite(FMDB)和线程时的EXC_BAD_ACCESS

dan*_*kbx 7 sqlite iphone exc-bad-access objective-c fmdb

我正在使用FMDB处理我的数据库工作正常.该应用程序使用后台线程,它正在做一些工作,需要访问数据库.同时主线程需要在同一个数据库上运行一些查询.FMDB本身有一个小锁定系统,但是,我在我的类中添加了另一个.

只有当我的类指示数据库未使用时,才会执行每个查询.执行操作后,数据库将解锁.只要负载不是太高,这就可以正常工作.当我在主线程上运行的线程访问大量数据时,会发生EXC_BAD_ACCESS错误.

这是看起来:

- (BOOL)isDatabaseLocked {
    return isDatabaseLocked;
}

- (Pile *)lockDatabase {
    isDatabaseLocked = YES;
    return self;        
}

- (FMDatabase *)lockedDatabase {
    @synchronized(self) {
        while ([self isDatabaseLocked]) {
            usleep(20);
            //NSLog(@"Waiting until database gets unlocked...");
        }
        isDatabaseLocked = YES;
        return self.database;       
    }
}

- (Pile *)unlockDatabase {
    isDatabaseLocked = NO;
    return self;            
}
Run Code Online (Sandbox Code Playgroud)

调试器说错误发生[FMResultSet next]在该行

rc = sqlite3_step(statement.statement);
Run Code Online (Sandbox Code Playgroud)

我仔细检查了所有保留计数,此时所有对象都存在.同样,它只发生在主线程在后台线程运行时启动大量查询时(它本身总是产生很大的负载).错误总是由主线程产生,而不是由后台线程产生.

我的最后一个想法是两个线程同时运行lockedDatabase,这样他们就可以得到一个数据库对象.这就是我通过"@synchronized(self)"添加互斥锁定的原因.但是,这没有帮助.

有人有线索吗?

Rik*_*nna 6

SQLite提供了更简单的序列化.通过设置sqlite_config()选项SQLITE_CONFIG_SERIALIZED,您可能会避免大多数这类令人头疼的问题.在与线程问题作斗争很长一段时间之后,我发现了这一点.

这是你如何使用它,你可以把它放在FMDatabase的init方法中......

    if (sqlite3_config(SQLITE_CONFIG_SERIALIZED) == SQLITE_ERROR) {
        NSLog(@"couldn't set serialized mode");
    }
Run Code Online (Sandbox Code Playgroud)

有关详细信息,请参阅有关threadsafetyserialized模式的SQLite文档.


Nic*_*ott 2

您应该在函数unlockDatabase 和lockDatabase 以及isDatabaseLocked 周围添加同步包装器——并不总是保证变量的存储或检索是原子的。当然,如果你这样做,你会想要将睡眠移到同步块之外,否则你会死锁。这本质上是一个自旋锁——它不是最有效的方法。

- (FMDatabase *)lockedDatabase {
    do
    {
        @synchronized(self) {
            if (![self isDatabaseLocked]) {
                isDatabaseLocked = YES;
                return self.database; 
            }
        }
        usleep(20);      
    }while(true); // continue until we get a lock
}
Run Code Online (Sandbox Code Playgroud)

您是否确保在调用unlockDatabase后不使用FMDatabase对象?您可能需要考虑句柄模式 - 创建一个包装 FMDatabase 对象的对象,并且只要它存在,就持有数据库的锁。在 init 中,您声明了锁,而在 dealloc 中,您可以释放该锁。那么你的客户端代码就不需要担心调用各种锁定/解锁函数,并且你不会意外地搞砸。尝试使用 NSMutex 而不是 @synchronized 块,请参阅http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html#//apple_ref/doc/uid/10000057i-CH8 -SW16