3

I'm wondering how I can make access to a __block qualified var thread-safe within the context of a method.

Example:

__block NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

for (int i=0; i<20; i++) {
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        [dictionary setObject:@"test" forKey:@"test"];
    }];
    [someConcurrentQueue addOperation:operation];
}

Here the operation is added to a concurrent queue and the dictionary var will potentially be accessed from different threads at the same time.

Is this safe? If not, how do I make access to dictionary safe?

firstresponder
  • 5,000
  • 8
  • 32
  • 38
  • 6
    I don't believe `__block` is necessary here because you are not changing what `dictionary` refers to, only internal properties of the object. I'm not sure about your concurrency issue, short of the obvious solution of simply using a serial queue or wrapping the unsafe operation in another block that is dispatched to a single serial queue. – UIAdam Feb 26 '12 at 08:17

2 Answers2

5

As UIAdam said in his comment, __block isn't doing anything for you here; you're mutating the dictionary, not assigning to the variable. The variable will continue to point to the same dictionary forever.

In fact, __block may actually hurt you here, since it means the variable will not be captured by the block. If you're not using ARC, this means the dictionary will not be retained, and the block may be sending messages to a dead object soon. I'm not sure offhand whether ARC changes this. At any rate, you should leave __block off of this variable; if nothing else, the code more clearly expresses your intent without it.

As for your actual question, about thread-safety, this code is not safe. According to the Thread-Safety Summary, the mutable collection classes are not thread-safe: you must send messages to a mutable collection from no more than one thread at a time. Synchronization would be one way; setting the queue's max concurrent operation count to 1 would be another.

Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • 3
    In ARC mode, `dictionary` _will_ be retained. Here is more info on this: http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html (see 'Blocks' part) – ivanzoid Feb 26 '12 at 08:53
4

this is not thread safe but nothing to do with __block keyword because you are only read but not write to it.

the easiest way to make it thread safe is using @synchronized keyword

__block NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];

for (int i=0; i<20; i++) {
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        @synchronized(dictionary) {
            [dictionary setObject:@"test" forKey:@"test"];
        }
    }];
    [someConcurrentQueue addOperation:operation];
}

or you can use NSLock or any types of lock

should read this for more information https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html

Bryan Chen
  • 45,816
  • 18
  • 112
  • 143