Objective c NSMutableDictionary线程安全

Objective c NSMutableDictionary线程安全,objective-c,nsmutabledictionary,Objective C,Nsmutabledictionary,我对使用NSMutableDictionary时的线程安全性有疑问 主线程正在从NSMutableDictionary读取数据,其中: 键是NSString 值为UIImage 异步线程正在将数据写入上述字典(使用NSOperationQueue) 如何使上述词典线程安全 我应该使NSMutableDictionary属性原子化吗?或者我需要做任何额外的更改吗 @property(retain)NSMutableDictionary*dicNamesWithPhotosNSMutableDi

我对使用
NSMutableDictionary
时的线程安全性有疑问

主线程正在从
NSMutableDictionary
读取数据,其中:

  • 键是
    NSString
  • 值为
    UIImage
异步线程正在将数据写入上述字典(使用
NSOperationQueue

如何使上述词典线程安全

我应该使
NSMutableDictionary
属性
原子化吗?或者我需要做任何额外的更改吗


@property(retain)NSMutableDictionary*dicNamesWithPhotos

NSMutableDictionary
不是为线程安全的数据结构而设计的,仅仅将属性标记为
原子的
,并不能确保底层数据操作实际上是以原子方式(以安全的方式)执行的

为确保以安全的方式完成每个操作,您需要使用锁保护字典上的每个操作:

// 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];

您应该考虑自己滚动<代码> NSCORCHONE/COD>,只需调用NStutabLy字典中的一个锁:

@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
请注意,因为每个操作都需要等待并持有锁,所以它的可伸缩性不强,但在您的情况下可能已经足够好了


如果要使用适当的线程库,可以使用多线程安全库,因为它们有
TKMutableDictionary
。我个人没有使用过它,而且它似乎是一个正在开发的库,但您可能想尝试一下。

经过一点研究,我想与您分享这篇文章:

在多线程应用程序中安全地使用集合类


看来诺普的答案终究不是一个解决方案。从线程的角度来看,这是可以的,但有一些关键的微妙之处。我不会在这里发布解决方案,但我想这篇文章中有一个很好的解决方案

我有两个使用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];
});

我不知道为什么有些人想要覆盖可变字典的设置和获取。

即使答案是正确的,也有一个优雅而不同的解决方案:

- (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;
}
当使用线程不安全对象时,副本很重要,这样可以避免由于意外释放变量而可能出现的错误。不需要线程安全实体

如果更多队列希望使用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\u barrier\u sync只是一个简单的dispatch\u sync


详细解释如下。

现在你可能会选择
@synchronized(object)

...
@synchronized(dictionary) {
    [dictionary setObject:image forKey:name];
}
...
@synchronized(dictionary) {
    [dictionary objectForKey:key];
}
...
@synchronized(dictionary) {
    [dictionary removeObjectForKey:key];
}

不再需要
NSLock
对象了

我不是多线程方面的专家,但我知道“原子”标志(@synthetic'd访问器的默认标志)不能保证线程安全。不过,当我第一次读到它时,我也有同样的想法。这看起来是一个很棒的方法,但我无法编译它。我在“retval”之前得到了
-(retval)forward:(SEL)SEL:(arglist)args
有什么想法吗?非常好的答案。现在已经过时了。改为使用队列。我在某处有一本非常简单的连载词典。我应该把它寄出去。邮件转发速度慢且脆弱。请跟进bbum的评论。在Mike Ash的博客上有一篇关于将GCD并发队列与NSMutableDictionary结合使用的有用文章:@bbum您是否曾经发布过您的序列化词典的示例?使用@sychronized使其安全如何?+1注意到在这种情况下锁定是不够的。我也曾被这个问题困扰过一次,
[[[dict objectForKey:key]retain]autorelease]
在多线程环境中,“技巧”确实是必要的。这个链接现在已经断了,技术说明是从2002年开始的。您最好使用-1,因为它几乎(但不是完全)是一个链接唯一的答案。这与@ProoEntry(atomic)dictionary不一样吗?不,这意味着用一个全新的实例替换dictionary是原子的。不改变字典中的任何一个元素。非常感谢!这是救命稻草。