From this Apple's document about NSCondition
, the usage of NSCondition
should be:
Thead 1:
[cocoaCondition lock];
while (timeToDoWork <= 0)
[cocoaCondition wait];
timeToDoWork--;
// Do real work here.
[cocoaCondition unlock];
Thread 2:
[cocoaCondition lock];
timeToDoWork++;
[cocoaCondition signal];
[cocoaCondition unlock];
And in the document of method signal
in NSConditon
:
You use this method to wake up one thread that is waiting on the condition. You may call this method multiple times to wake up multiple threads. If no threads are waiting on the condition, this method does nothing. To avoid race conditions, you should invoke this method only while the receiver is locked.
My question is:
I don't want the Thread 2 been blocked in any situation, so I removed the lock
and unlock
call in Thread 2. That is, Thread 2 can put as many work as it wish, Thread 1 will do the work one by one, if no more work, it wait (blocked). This is also a producer-consumer pattern, but the producer never been blocked.
But the way is not correct according to Apple's document So what things could possibly go wrong in this pattern? Thanks.
Failing to lock is a serious problem when multiple threads are accessing shared data. In the example from Apple's code, if Thread 2 doesn't lock the condition object then it can be incrementing
timeToDoWork
at the same time that Thread 1 is decrementing it. That can result in the results from one of those operations being lost. For example:Thread 1 reads the current value of
timeToDoWork
, gets 1Thread 2 reads the current value of
timeToDoWork
, gets 1Thread 2 computes the incremented value (
timeToDoWork
+ 1), gets 2Thread 1 computes the decremented value (
timeToDoWork
- 1), gets 0Thread 2 writes the new value of
timeToDoWork
, stores 2Thread 1 writes the new value of
timeToDoWork
, stores 0timeToDoWork
started at 1, was incremented and decremented, so it should end at 1, but it actually ends at 0. By rearranging the steps, it could end up at 2, instead. Presumably, the value oftimeToDoWork
represents something real and important. Getting it wrong would probably screw up the program.If your two threads are doing something as simple as incrementing and decrementing a number, they can do it without locks by using the atomic operation functions, such as
OSAtomicIncrement32Barrier()
andOSAtomicDecrement32Barrier()
. However, if the shared data is any more complicated than that (and it probably is in any non-trivial case), then they really need to use synchronization mechanisms such as condition locks.