Безопасность NSMutableDictionary
У меня есть вопрос о безопасности потоков при использовании NSMutableDictionary
.
Основной поток - это данные из NSMutableDictionary
где:
- ключ
NSString
- значение
UIImage
Асинхронный поток записывает данные в словарь (используя NSOperationQueue
)
Как сделать безопасный поток слов выше?
Должен ли я сделать свойство NSMutableDictionary
atomic
? Или мне нужно внести какие-либо дополнительные изменения?
@property(retain) NSMutableDictionary *dicNamesWithPhotos;
Ответы
Ответ 1
NSMutableDictionary
не предназначен для потокобезопасной структуры данных и просто маркирует свойство как atomic
, не гарантирует, что операции с базовыми данными фактически выполняются атомарно (безопасным образом).
Чтобы гарантировать, что каждая операция выполняется безопасным образом, вам нужно будет заблокировать каждую операцию в словаре с помощью блокировки:
// 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
Обратите внимание, что поскольку каждая операция требует ожидания блокировки и ее удержания, она не достаточно масштабируема, но может быть достаточно хорошей в вашем случае.
Если вы хотите использовать нужную библиотеку с резьбой, вы можете использовать библиотеку TransactionKit, поскольку они имеют TKMutableDictionary
, который является многопоточным безопасным библиотека. Я лично не использовал его, и кажется, что это незавершенная библиотека, но вы можете попробовать.
Ответ 2
после небольшого исследования я хочу поделиться с вами этой статьей:
Безопасное использование классов коллекций с помощью многопоточных приложений
http://developer.apple.com/library/mac/#technotes/tn2002/tn2059.html
Похоже, что notnoop ответ не может быть решением в конце концов. С точки зрения резьбы это нормально, но есть некоторые критические тонкости. Я не буду размещать здесь решение, но я думаю, что в этой статье есть хорошая.
Ответ 3
У меня есть два варианта использования nsmutabledictionary.
Один из них:
NSLock* lock = [[NSLock alloc] init];
[lock lock];
[object.dictionary setObject:image forKey:name];
[lock unlock];
Два:
//Let assume var image, name are setup properly
dispatch_async(dispatch_get_main_queue(),
^{
[object.dictionary setObject:image forKey:name];
});
Я не знаю, почему некоторые люди хотят перезаписать настройки и получить mutabledictionary.
Ответ 4
Даже ответ правильный, есть элегантное и другое решение:
- (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, объявите приватную очередь и измените установщик на:
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
Подробное объяснение в этой замечательной статье в блоге.