7

我正在使用 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;            
}

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

rc = sqlite3_step(statement.statement);

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

我的最后一个想法是两个线程同时运行lockedDatabase,这样它们就可以获得一个数据库对象。这就是我通过“@synchronized(self)”添加互斥锁的原因。然而,这并没有帮助。

有人有线索吗?

4

4 回答 4

6

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

下面是你的使用方法,你可以把它放在FMDatabase的init方法中...

    if (sqlite3_config(SQLITE_CONFIG_SERIALIZED) == SQLITE_ERROR) {
        NSLog(@"couldn't set serialized mode");
    }

有关更多信息,请参阅有关线程安全序列化模式的 SQLite 文档。

于 2012-07-29T17:38:37.107 回答
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
}

在调用 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

于 2010-06-29T21:48:23.230 回答
0

我遇到了这个问题,并且仅仅通过打开准备好的语句的缓存就能够消除这个问题。

FMDatabase *myDatabase = [FMDatabase databaseWithPath: pathToDatabase];
myDatabase.shouldCacheStatements = YES;
于 2015-04-17T19:00:21.463 回答
0

您也可以尝试 FMDatabaseQueue - 我专门为这种情况创建了它。我还没有尝试过,但我相当确定它适用于 iOS 4。

于 2012-05-19T01:18:21.550 回答