几个月后,我在我的应用中遇到了这个问题。我尝试了很多自制的解决方案,并会解释我在这里工作的内容,但我希望有人能提出我错过的更好的解决方案。
基本问题是:我(可能)有数千个项目需要我的应用随时访问。 NSMutableDictionary通常是我表示每个项目的第一种方法,因为每个项目可能有几个到几百个属性。但其余的要求使事情变得多毛:
我想使用CoreData,因为Apple非常喜欢它,但我遇到了很多问题。每个项目都没有明确的结构,因此没有好的方法来构建数据模型。此外,查询数据导致单个.sqlite文件成为瓶颈,这意味着当许多线程试图一次检索项目时,等待时间(滞后)很快就会变得荒谬。
我有一个有效的解决方案,但它有问题。这是代码的一大块,我将在下面解释它的作用
- (NSObject*) getValue:(NSString*)key {
@synchronized(self) {
if(!_cached_obj) { // private variable in this object
_cached_obj = [self loadFromDisk]; // simply loads the NSDictionary from a file
}
_last_access = time(nil);//don't release for a while
return [_cached_obj valueForKey:key];
}
}
- (void) setValue:(NSObject*)value forKey:(NSString*)key {
@synchronized(self) {
[self getValue:key];//ensures the cache is active
[_cached_obj setValue:value forKey:key];
_needs_save = true;
}
}
- (void) clean {
if(!_cached_obj)
return;
@synchronized(self) {
if(_needs_save)
{
[self writeToFile];//writes the cache obj to a file
_needs_save = NO;
}
NSTimeInterval elapsed = time(nil) - _last_access;
if(elapsed > 20)
{
[_cached_obj release];
_cached_obj = nil;
}
}
}
我不喜欢我的方法是基于我对@synchronized的使用,在信号量上有很多等待。偶尔,这也意味着主线程在等待磁盘读/写时会阻塞,这很痛苦。
我是否缺少更好的数据结构或存储机制?
谢谢!
编辑:更多信息: “getValue”函数返回的速度也非常重要,即使它没有阻塞主线程。例如,考虑我在后台线程上搜索10k项目的场景。我需要从每个10k对象中获取一个值。使用我当前的机制,它可以正常工作,但是从磁盘上加载每个非缓存对象非常耗时,最终在我的iPhone 4上花费约20秒。我知道这可能只是“我必须付出的代价。 “但是,将数据存储在较小的块中可能会有所帮助吗?例如,不要将整个项目存储为字典,而是存储为不同对象的集合。
答案 0 :(得分:1)
据我了解,您分析了自己的应用,并且配置文件显示@synchronize块是最大的性能瓶颈。正确?
好吧,我并不会感到惊讶:你按照指出的那样在按住互斥锁的同时读写文件。此外,您只允许同时使用一个线程,而您可以轻松地允许许多读者或一个编写者访问您的缓存。
识别锁定操作:
那么,基本操作是:
很容易确定这些简单操作的并发性,然后重新修改锁定以确保一切都能很好地相互协作。
您可以允许许多读者或一个编写者访问缓存。一个线程可以在磁盘上读取(或写入),而无需锁定缓存。从磁盘读取的值将在稍后作为编写器在缓存中设置。因此,缓存的一个读写锁,以及文件的互斥锁。设定值序列也有点令人费解。我没有看到从文件中读取旧值以立即替换它的意义。如果您需要准备好缓存数据结构,只需确保它们不会触发文件操作。
所有这些也可以使用GCD实现,如果不是全部,则可以避免大部分锁定。
在没有引入大量复杂性或改变应用程序线程模型的情况下,还有足够的空间来减少冲突。我认为GCD提供了更多的机会,但你必须考虑队列和操作而不是线程,这一点并不总是很容易。
我不会说重新修改锁就足够了,你可能还需要改进数据读取和保存到磁盘的方式,但是从锁开始。你可能会感到惊讶。