Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Generally, <code>@synchronized</code> guarantees thread safety, but only when used correctly. It is also safe to acquire the lock recursively, albeit with limitations I detail in my answer <a href="https://stackoverflow.com/questions/10163456/locked-up-waiting-for-synchronized/19429880#19429880">here</a>.</p> <p>There are several common ways to use <code>@synchronized</code> wrong. These are the most common:</p> <p>Using <code>@synchronized</code> to ensure atomic object creation.</p> <pre><code>- (NSObject *)foo { @synchronized(_foo) { if (!_foo) { _foo = [[NSObject alloc] init]; } return _foo; } } </code></pre> <p>Because <code>_foo</code> will be nil when the lock is first acquired, no locking will occur and multiple threads can potentially create their own <code>_foo</code> before the first completes.</p> <p>Using <code>@synchronized</code> to lock on a new object each time.</p> <pre><code>- (void)foo { @synchronized([[NSObject alloc] init]) { [self bar]; } } </code></pre> <p>I've seen this code quite a bit, as well as the C# equivalent <code>lock(new object()) {..}</code>. Since it attempts to lock on a new object each time, it will always be allowed into the critical section of code. This is not some kind of code magic. It does absolutely nothing to ensure thread safety.</p> <p>Lastly, locking on <code>self</code>.</p> <pre><code>- (void)foo { @synchronized(self) { [self bar]; } } </code></pre> <p>While not by itself a problem, if your code uses any external code or is itself a library, it can be an issue. While internally the object is known as <code>self</code>, it externally has a variable name. If the external code calls <code>@synchronized(_yourObject) {...}</code> and you call <code>@synchronized(self) {...}</code>, you may find yourself in deadlock. It is best to create an internal object to lock upon that is not exposed outside of your object. Adding <code>_lockObject = [[NSObject alloc] init];</code> inside your init function is cheap, easy, and safe.</p> <p>EDIT:</p> <p>I still get asked questions about this post, so here is an example of why it is a bad idea to use <code>@synchronized(self)</code> in practice.</p> <pre><code>@interface Foo : NSObject - (void)doSomething; @end @implementation Foo - (void)doSomething { sleep(1); @synchronized(self) { NSLog(@"Critical Section."); } } // Elsewhere in your code dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); Foo *foo = [[Foo alloc] init]; NSObject *lock = [[NSObject alloc] init]; dispatch_async(queue, ^{ for (int i=0; i&lt;100; i++) { @synchronized(lock) { [foo doSomething]; } NSLog(@"Background pass %d complete.", i); } }); for (int i=0; i&lt;100; i++) { @synchronized(foo) { @synchronized(lock) { [foo doSomething]; } } NSLog(@"Foreground pass %d complete.", i); } </code></pre> <p>It should be obvious to see why this happens. Locking on <code>foo</code> and <code>lock</code> are called in different orders on the foreground VS background threads. It's easy to say that this is bad practice, but if <code>Foo</code> is a library, the user is unlikely to know that the code contains a lock. </p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload