3

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.

Albert Zhang
  • 757
  • 1
  • 8
  • 19

1 Answers1

2

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 1
Thread 2 reads the current value of timeToDoWork, gets 1
Thread 2 computes the incremented value (timeToDoWork + 1), gets 2
Thread 1 computes the decremented value (timeToDoWork - 1), gets 0
Thread 2 writes the new value of timeToDoWork, stores 2
Thread 1 writes the new value of timeToDoWork, stores 0

timeToDoWork 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 of timeToDoWork 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() and OSAtomicDecrement32Barrier(). 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.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thanks Ken. The actual situation I'm working on is a producer-cusumer pattern, I use the NSMutableArray to implement it. Where one thread add object to it, another thread take off objects. When no objects, the consumer thread wait. But I don't want the producer thread been blocked. Is there any way to implement this? – Albert Zhang Jan 16 '15 at 06:31
  • 1
    You ***have to*** synchronize access to an `NSMutableArray`. It is not safe to mutate it while other threads might be accessing it. One mechanism you can use to synchronize that access is to put all accesses as tasks on a serial GCD queue. Pushes can be async. That would allow the producer to continue, using the GCD queue as a secondary, thread-safe data structure of sorts. However, what sense does it make for your producer to run ahead of your consumer indefinitely? You eventually need it to stop and wait for the consumer to catch up or you're going to consume memory and other resources. – Ken Thomases Jan 16 '15 at 06:54
  • Yes I get it, really appreciated. With respect to my situation, it is a message sender, the user can continuous tap or move on the screen to produce many many messages, and another thread - the consumer - to send the messages out. So the main thread (the producer) can't be blocked. – Albert Zhang Jan 16 '15 at 07:17
  • You can dispatch your producer onto an asynchronous serial queue. This can then block without impacting your UI – Paulw11 Jan 16 '15 at 12:16