web-dev-qa-db-ja.com

NSMutableDictionaryスレッドセーフティ

NSMutableDictionaryの使用中のスレッドセーフティについて質問があります。

メインスレッドはNSMutableDictionaryからデータを読み取っています。

  • キーはNSStringです
  • 値はUIImageです

非同期スレッドが上記の辞書にデータを書き込んでいます(NSOperationQueueを使用)

上記の辞書スレッドを安全にするにはどうすればよいですか?

NSMutableDictionaryプロパティをatomicにする必要がありますか?または、追加の変更を行う必要がありますか?

@property(retain) NSMutableDictionary *dicNamesWithPhotos;

39
Raj

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

ロックを保持しながらNSMutableDictionaryへの呼び出しを委任するだけの独自のNSDictionaryをロールすることを検討する必要があります。

@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があるため、 TransactionKitライブラリ を使用できます。個人的には使っていないのですが、未完成のライブラリのようですが、試してみたくなるかもしれません。

73
notnoop

Nsmutabledictionaryを使用する方法は2つあります。

1つは次のとおりです。

NSLock* lock = [[NSLock alloc] init];
[lock lock];
[object.dictionary setObject:image forKey:name];
[lock unlock];

2つは次のとおりです。

//Let's assume var image, name are setup properly
dispatch_async(dispatch_get_main_queue(), 
^{ 
        [object.dictionary setObject:image forKey:name];
});

一部の人々が設定と変更可能な辞書の取得を上書きしたい理由を知りません。

1
gigir

今日では、おそらく代わりに@synchronized(object)を使用します。

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

NSLockオブジェクトはもう必要ありません

1
P.Melch

正解でさえ、エレガントで異なるソリューションがあります:

- (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

詳細はこちら 素晴らしいブログ記事

1
BootMaker

少し調査した後、この記事を皆さんと共有したいと思います。

マルチスレッドアプリケーションでコレクションクラスを安全に使用する http://developer.Apple.com/library/mac/#technotes/tn2002/tn2059.html

Notnoopの答えは結局のところ解決策ではないようです。スレッドの観点からは問題ありませんが、いくつかの重要な微妙な点があります。私はここに解決策を投稿しませんが、この記事には良い解決策があると思います。

1
Adrian