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

详细的解释是在这个奇妙的博客文章 。