NSMutableDictionary线程安全
我在使用NSMutableDictionary
遇到了线程安全问题。
主线程是从NSMutableDictionary
中读取数据,其中:
- 关键是
NSString
- 值是
UIImage
asynchronous线程正在向上面的字典写入数据(使用NSOperationQueue
)
如何使上述字典线程安全?
我应该使NSMutableDictionary
属性atomic
? 还是需要进行其他更改?
@property(retain) NSMutableDictionary *dicNamesWithPhotos;
NSMutableDictionary
不是devise为线程安全的数据结构,只是简单地将该属性标记为atomic
,并不能确保底层的数据操作实际上是以primefaces方式(以安全的方式)执行的。
为了确保每个操作都以安全的方式完成,您需要用锁locking字典上的每个操作:
// in initialization self.dictionary = [[NSMutableDictionary alloc] init]; // create a lock object for the dictionary self.dictionary_lock = [[NSLock alloc] init]; // at every access or modification: [object.dictionary_lock lock]; [object.dictionary setObject:image forKey:name]; [object.dictionary_lock unlock];
你应该考虑滚动你自己的NSDictionary
,只是把持有锁的委托调用到NSMutableDictionary:
@interface SafeMutableDictionary : NSMutableDictionary { NSLock *lock; NSMutableDictionary *underlyingDictionary; } @end @implementation SafeMutableDictionary - (id)init { if (self = [super init]) { lock = [[NSLock alloc] init]; underlyingDictionary = [[NSMutableDictionary alloc] init]; } return self; } - (void) dealloc { [lock_ release]; [underlyingDictionary release]; [super dealloc]; } // forward all the calls with the lock held - (retval_t) forward: (SEL) sel : (arglist_t) args { [lock lock]; @try { return [underlyingDictionary performv:sel : args]; } @finally { [lock unlock]; } } @end
请注意,因为每个操作都需要等待locking并保持不变,所以它不是可扩展的,但对于您的情况可能会足够好。
如果你想使用一个正确的线程库,你可以使用TransactionKit库,因为它们有一个TKMutableDictionary
,它是一个multithreading安全库。 我个人并没有使用它,似乎这是一个在进步的图书馆,但你可能想试试看。
经过一点研究,我想和大家分享一下这篇文章:
使用multithreading应用程序安全地使用集合类http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html
看起来notnoop的答案毕竟可能不是解决scheme。 从线程的angular度来看,这是好的,但有一些关键的微妙之处。 我不会在这里发布一个解决scheme,但我猜这里有一个很好的解决scheme。
我有两个select使用nsmutabledictionary。
一个是:
NSLock* lock = [[NSLock alloc] init]; [lock lock]; [object.dictionary setObject:image forKey:name]; [lock unlock];
二是:
//Let's assume var image, name are setup properly dispatch_async(dispatch_get_main_queue(), ^{ [object.dictionary setObject:image forKey:name]; });
我不知道为什么有些人想覆盖设置和获取mutabledictionary。
即使答案是正确的,但有一个优雅的和不同的解决scheme:
- (id)init { self = [super init]; if (self != nil) { NSString *label = [NSString stringWithFormat:@"%@.isolation.%p", [self class], self]; self.isolationQueue = dispatch_queue_create([label UTF8String], NULL); label = [NSString stringWithFormat:@"%@.work.%p", [self class], self]; self.workQueue = dispatch_queue_create([label UTF8String], NULL); } return self; } //Setter, write into NSMutableDictionary - (void)setCount:(NSUInteger)count forKey:(NSString *)key { key = [key copy]; dispatch_async(self.isolationQueue, ^(){ if (count == 0) { [self.counts removeObjectForKey:key]; } else { self.counts[key] = @(count); } }); } //Getter, read from NSMutableDictionary - (NSUInteger)countForKey:(NSString *)key { __block NSUInteger count; dispatch_sync(self.isolationQueue, ^(){ NSNumber *n = self.counts[key]; count = [n unsignedIntegerValue]; }); return count; }
使用线程不安全的对象时,副本很重要,这样可以避免由于意外释放variables而可能出现的错误。 不需要线程安全的实体。
如果更多的队列希望使用NSMutableDictionary声明一个私有队列,并将setter更改为:
self.isolationQueue = dispatch_queue_create([label UTF8String], DISPATCH_QUEUE_CONCURRENT); - (void)setCount:(NSUInteger)count forKey:(NSString *)key { key = [key copy]; dispatch_barrier_async(self.isolationQueue, ^(){ if (count == 0) { [self.counts removeObjectForKey:key]; } else { self.counts[key] = @(count); } }); }
重要!
你必须设置一个自己的专用队列, dispatch_barrier_sync只是一个简单的dispatch_sync
详细的解释是在这个奇妙的博客文章 。