Reputation: 1226
I have been using NSCondition lately, and I really don't understand the lock and unlock part.
For example,
NSCondition lock = [NSCondition new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Do stuff
[lock signal];
};
[lock wait];
//Do rest
This is working fine. What difference does it make from this..
NSCondition lock = [[NSCondition alloc] init];
[lock lock];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//Do stuff
[lock signal];
[lock unlock];
};
[lock wait];
//Do rest
Upvotes: 3
Views: 2559
Reputation: 38797
You must hold the lock on a condition variable before waiting. Waiting releases the lock, then is guaranteed to block until it has been signalled and the lock has been re-taken. Note that if multiple threads are waiting, only one will be notified of the signal, the others will continue to wait for further signals. Alternatively, if the predicate will remain true after execution, then the signal can be broadcast to all threads.
The lock is a mutual exclusion lock (mutex), meaning only one thread can hold the lock at any one time. As well as using this to protect whatever you're using the condition variable for, it also protects the internal workings of the condition variable. If you attempted to use it without the lock then there would be race conditions on checking/setting the condition across multiple threads.
For correct use of a condition variable, neither of your examples are correct. The whole point is to be protecting the state of a predicate. Here is an example is based on the pseudo-code from the documentation.
NSCondition condvar = [NSCondition new];
__block BOOL workRequired = NO;
// something somewhere else does this at some point ^{
[condvar lock];
workRequired = YES;
[condvar signal];
[condvar unlock];
};
[condvar lock];
while (!workRequired) {
[condvar wait];
}
// Do the work
workRequired = NO;
[condvar unlock];
In your example, you don't have any condition beyond "the block finished". In which case you should just use dispatch_sync.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do stuff
};
// Do rest
If for some reason you don't know if you're already running on that queue, then a semaphore will suffice.
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0L);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Do stuff
dispatch_semaphore_signal(semaphore);
};
dispatch_semaphore_wait(semaphore);
// Do rest
Upvotes: 4