44

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

メイン スレッドは次のNSMutableDictionary場所からデータを読み取ります。

  • キーはNSString
  • 値はUIImage

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

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

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

@property(retain) NSMutableDictionary *dicNamesWithPhotos;

4

5 に答える 5

77

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個人的には使ったことがなく、開発中のライブラリのようですが、試してみてください。

于 2009-12-31T19:52:22.680 に答える
6

@synchronized(object)今日では、おそらく代わりに行くでしょう。

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

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

于 2018-08-02T09:04:01.587 に答える
2

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

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

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

于 2011-05-06T19:52:49.123 に答える
1

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

mutabledictionary の設定と取得を上書きしたい人がいる理由がわかりません。

于 2016-07-14T09:14:14.167 に答える
1

答えが正しいとしても、エレガントで異なる解決策があります。

- (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);
    }
});
}

重要!

それなしで独自のプライベートキューを設定する必要があります

詳細な説明は、このすばらしいブログ記事にあります。

于 2016-08-05T13:57:16.437 に答える