Learn Objective-C on the Mac - Mark Dalrymple [153]
A common concern when dealing with threads is the idea of code being “thread-safe.” Some software libraries are written with concurrency in mind, and have all their critical sections properly protected with mutexes. Some code libraries simply don’t. For instance, in Cocoa, the AppKit framework (containing the classes specific to building GUI applications, such as NSApplication, NSView and all its subclasses, and the like) is for the most part not thread-safe. This means that in a running Cocoa application, all method calls that deal with any AppKit objects should be executed from within the same thread, which is commonly known as the “main thread.” If you access AppKit objects from another thread, all bets are off. You are likely to encounter seemingly inexplicable bugs. By default, the main thread is where all the action of your Cocoa app (such as dealing with actions triggered by user events) occurs, so for simple applications it’s nothing you need to worry about. Action methods triggered by a user are already running in the main thread. Up to this point in the book, our code has been running exclusively on the main thread, but that’s about to change.
Units of Work
The problem with the threading model just described is that for the average programmer, writing error-free, multi-threaded code is nearly impossible. This is not meant as a critique of our industry or of the average programmer’s abilities; it’s simply an observation. The complex interactions you have to account for in your code when synchronizing data and actions across multiple threads are really just too much for most people to tackle. Imagine that 5 percent of all people have the capacity to write software at all. Only a small fraction of those 5 percent are really up to the task of writing heavy-duty multi-threaded applications. Even people who have done it successfully will often advise others to not follow their example!
Fortunately, all hope is not yet lost. It is possible to implement some concurrency without too much low-level thread-twisting. Just like we have the ability to display data on the screen without directly poking bits into video RAM, and to read data from disk without interfacing directly with disk controllers, software abstractions exist that let us run our code on multiple threads without requiring us to do much directly with the threads at all. The solutions that Apple encourages us to use are centered around the ideas of splitting up long-running tasks into units of work, and putting those units into queues for execution. The system manages the queues for us, executing units of work on multiple threads for us. We don’t need to start and manage the background threads directly, and are freed from much of the “bookkeeping” that’s usually involved in implementing concurrent applications. The system takes care of that for us.
Operation Queues
Since the release of Leopard, Apple has provided us with a pair of classes called NSOperation and NSOperationQueue that work together to provide operation queues. The idea is that you split your computational tasks into chunks or units of work, wrap each of them up in an NSOperation, and put each operation into an NSOperationQueue. You can also establish interoperation dependencies, specifying that an operation won’t begin executing until another one is complete. The NSOperationQueue then takes care of these units the best it can, using the order that operations were added to the queue, along with the dependencies you specified, to determine its course of action. If the dependencies you specify allow some operations to execute at the same time, and there are enough cores available to run them, the operation queue will use multiple threads to execute multiple operations simultaneously.
Vitalizing