objective-c NSMutableDictionary 线程安全
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1986736/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
NSMutableDictionary thread safety
提问by Raj
I have a question on thread safety while using NSMutableDictionary.
我在使用NSMutableDictionary.
The main thread is reading data from NSMutableDictionarywhere:
主线程从以下NSMutableDictionary位置读取数据:
- key is
NSString - value is
UIImage
- 关键是
NSString - 价值是
UIImage
An asynchronous thread is writing data to above dictionary (using NSOperationQueue)
异步线程正在将数据写入上述字典(使用NSOperationQueue)
How do I make the above dictionary thread safe?
如何使上述字典线程安全?
Should I make the NSMutableDictionaryproperty atomic? Or do I need to make any additional changes?
我应该做NSMutableDictionary财产atomic吗?或者我需要做任何额外的改变吗?
@property(retain) NSMutableDictionary *dicNamesWithPhotos;
@property(retain) NSMutableDictionary *dicNamesWithPhotos;
回答by notnoop
NSMutableDictionaryisn't designed to be thread-safe data structure, and simply marking the property as atomic, doesn't ensure that the underlying data operations are actually performed atomically (in a safe manner).
NSMutableDictionary不是被设计为线程安全的数据结构,并且简单地将属性标记为atomic,并不能确保底层数据操作实际上以原子方式(以安全方式)执行。
To ensure that each operation is done in a safe manner, you would need to guard each operation on the dictionary with a lock:
为了确保每个操作都以安全的方式完成,您需要用锁保护字典上的每个操作:
// 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];
You should consider rolling your own NSDictionarythat simply delegates calls to NSMutableDictionary while holding a lock:
你应该考虑滚动你自己的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
Please note that because each operation requires waiting for the lock and holding it, it's not quite scalable, but it might be good enough in your case.
请注意,因为每个操作都需要等待锁定并持有它,所以它的可扩展性不是很好,但在您的情况下它可能已经足够了。
If you want to use a proper threaded library, you can use TransactionKit libraryas they have TKMutableDictionarywhich is a multi-threaded safe library. I personally haven't used it, and it seems that it's a work in progress library, but you might want to give it a try.
如果您想使用适当的线程库,您可以使用TransactionKit 库,因为它们TKMutableDictionary是多线程安全库。我个人没有使用过它,它似乎是一个正在进行中的库,但您可能想尝试一下。
回答by Adrian
after a little bit of research I want to share with you this article :
经过一些研究,我想与您分享这篇文章:
Using collection classes safely with multithreaded applications http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html
在多线程应用程序中安全地使用集合类 http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html
It looks like notnoop's answer may not be a solution after all. From threading perspective it is ok, but there are some critical subtleties. I will not post here a solution but I guess that there is a good one in this article.
看起来 notnoop 的答案毕竟可能不是解决方案。从线程的角度来看是可以的,但有一些关键的微妙之处。我不会在这里发布解决方案,但我想本文中有一个很好的解决方案。
回答by gigir
I have two options to using nsmutabledictionary.
我有两种使用 nsmutabledictionary 的选择。
One is:
一种是:
NSLock* lock = [[NSLock alloc] init];
[lock lock];
[object.dictionary setObject:image forKey:name];
[lock unlock];
Two is:
二是:
//Let's assume var image, name are setup properly
dispatch_async(dispatch_get_main_queue(),
^{
[object.dictionary setObject:image forKey:name];
});
I dont know why some people want to overwrite setting and getting of mutabledictionary.
我不知道为什么有些人想覆盖可变字典的设置和获取。
回答by BootMaker
Even the answer is correct, there is an elegant and different solution:
即使答案是正确的,也有一个优雅而不同的解决方案:
- (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;
}
The copy is important when using thread unsafe objects, with this you could avoid the possible error because of unintended release of the variable. No need for thread safe entities.
使用线程不安全对象时,副本很重要,这样您就可以避免由于意外释放变量而导致的可能错误。不需要线程安全实体。
If more queue would like to use the NSMutableDictionary declare a private queue and change the setter to:
如果更多队列想使用 NSMutableDictionary 声明一个私有队列并将设置器更改为:
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);
}
});
}
IMPORTANT!
重要的!
You have to set an own private queue without it the dispatch_barrier_syncis just a simple dispatch_sync
您必须在没有它的情况下设置自己的私有队列dispatch_barrier_sync只是一个简单的dispatch_sync
Detailed explanation is in this marvelous blog article.
详细解释在这篇精彩的博客文章中。
回答by P.Melch
Nowadays you'd probably go for @synchronized(object)instead.
现在你可能会去@synchronized(object)。
...
@synchronized(dictionary) {
[dictionary setObject:image forKey:name];
}
...
@synchronized(dictionary) {
[dictionary objectForKey:key];
}
...
@synchronized(dictionary) {
[dictionary removeObjectForKey:key];
}
No need for the NSLockobject any more
不再需要NSLock对象

