Threads introduce a tremendous amount of overhead to your process, both in terms of memory consumption and CPU time. 除非有必要,尽量不要使用Threads.
Run Loop:
A run loop is a piece of infrastructure used to manage events arriving asynchronously on a thread. A run loop works by monitoring one or more event sources for the thread. As events arrive, the system wakes up the thread and dispatches the events to the run loop, which then dispatches them to the handlers you specify. If no events are present and ready to be handled, the run loop puts the thread to sleep.
You are not required to use a run loop with any threads you create but doing so can provide a better experience for the user. Run loops make it possible to create long-lived threads that use a minimal amount of resources. Because a run loop puts its thread to sleep when there is nothing to do, it eliminates the need for polling, which wastes CPU cycles and prevents the processor itself from sleeping and saving power.
To configure a run loop, all you have to do is launch your thread, get a reference to the run loop object, install your event handlers, and tell the run loop to run. The infrastructure provided by both Cocoa and Carbon handles the configuration of the main thread’s run loop for you automatically. If you plan to create long-lived secondary threads, however, you must configure the run loop for those threads yourself.
Synchronization Tools:
One of the hazards of threaded programming is resource contention among multiple threads. If multiple threads try to use or modify the same resource at the same time, problems can occur. One way to alleviate the problem is to eliminate the shared resource altogether and make sure each thread has its own distinct set of resources on which to operate. When maintaining completely separate resources is not an option though, you may have to synchronize access to the resource using locks, conditions, atomic operations, and other techniques.There are two ways to create a thread using the class:
-
Use the
class method to spawn the new thread.
-
Create a new
NSThread
object and call itsstart
method. (Supported only in iOS and Mac OS X v10.5 and later.)
NSThread
object whose thread is currently running, one way you can send messages to that thread is to use the
method of almost any object in your application.
method creates a new detached thread and uses the specified method as the entry point for the new thread. The effect of calling this method is the same as if you called the
method of
with the current object, selector, and parameter object as parameters. The new thread is spawned immediately using the default configuration and begins running. Inside the selector, you must configure the thread just as you would any thread. For example, you would need to set up an autorelease pool (if you were not using garbage collection) and configure the thread’s run loop if you planned to use it. - (void)myThreadMainRoutine |
{ |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool |
// Do thread work here. |
[pool release]; // Release the objects in the pool. |
} |
Listing 2-3 Checking for an exit condition during a long job
- (void)threadMainRoutine |
{ |
BOOL moreWorkToDo = YES; |
BOOL exitNow = NO; |
NSRunLoop* runLoop = [NSRunLoop currentRunLoop]; |
// Add the exitNow BOOL to the thread dictionary. |
NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary]; |
[threadDict setValue:[NSNumber numberWithBool:exitNow] forKey:@"ThreadShouldExitNow"]; |
// Install an input source. |
[self myInstallCustomInputSource]; |
while (moreWorkToDo && !exitNow) |
{ |
// Do one chunk of a larger body of work here. |
// Change the value of the moreWorkToDo Boolean when done. |
// Run the run loop but timeout immediately if the input source isn't waiting to fire. |
[runLoop runUntilDate:[NSDate date]]; |
// Check to see if an input source handler changed the exitNow value. |
exitNow = [[threadDict valueForKey:@"ThreadShouldExitNow"] boolValue]; |
} |
} |
while
or for
loop that drives the run loop. Within your loop, you use a run loop object to "run” the event-processing code that receives events and calls the installed handlers. For secondary threads, you need to decide whether a run loop is necessary, and if it is, configure and start it yourself. You do not need to start a thread’s run loop in all cases. For example, if you use a thread to perform some long-running and predetermined task, you can probably avoid starting the run loop. Run loops are intended for situations where you want more interactivity with the thread. For example, you need to start a run loop if you plan to do any of the following:
-
Use ports or custom input sources to communicate with other threads.
-
Use timers on the thread.
-
Use any of the
performSelector
… methods in a Cocoa application. -
Keep the thread around to perform periodic tasks.
To get the run loop for the current thread, you use one of the following:
-
In a Cocoa application, use the
class method of
to retrieve an
NSRunLoop
object. -
Use the
function.
Although they are not toll-free bridged types, you can get a CFRunLoopRef
opaque type from an NSRunLoop
object when needed. The NSRunLoop
class defines a method that returns a
CFRunLoopRef
type that you can pass to Core Foundation routines. Because both objects refer to the same run loop, you can intermix calls to the NSRunLoop
object andCFRunLoopRef
opaque type as needed.
Starting the run loop is necessary only for the secondary threads in your application. A run loop must have at least one input source or timer to monitor. If one is not attached, the run loop exits immediately.
There are several ways to start the run loop, including the following:
-
Unconditionally
-
With a set time limit
-
In a particular mode
Entering your run loop unconditionally is the simplest option, but it is also the least desirable. Running your run loop unconditionally puts the thread into a permanent loop, which gives you very little control over the run loop itself. You can add and remove input sources and timers, but the only way to stop the run loop is to kill it. There is also no way to run the run loop in a custom mode.
Instead of running a run loop unconditionally, it is better to run the run loop with a timeout value. When you use a timeout value, the run loop runs until an event arrives or the allotted time expires. If an event arrives, that event is dispatched to a handler for processing and then the run loop exits. Your code can then restart the run loop to handle the next event. If the allotted time expires instead, you can simply restart the run loop or use the time to do any needed housekeeping.
In addition to a timeout value, you can also run your run loop using a specific mode. Modes and timeout values are not mutually exclusive and can both be used when starting a run loop.
There are two ways to make a run loop exit before it has processed an event:
-
Configure the run loop to run with a timeout value.
-
Tell the run loop to stop.
Using a timeout value is certainly preferred, if you can manage it. Specifying a timeout value lets the run loop finish all of its normal processing, including delivering notifications to run loop observers, before exiting.
Stopping the run loop explicitly with the function produces a result similar to a timeout. The run loop sends out any remaining run-loop notifications and then exits. The difference is that you can use this technique on run loops you started unconditionally.
NSLock
object is used to coordinate the operation of multiple threads of execution within the same application. An NSLock
object can be used to mediate access to an application’s global data or to protect a critical section of code, allowing it to run atomically. You should not use this class to implement a recursive lock. Calling the lock
method twice on the same thread will lock up your thread permanently. Use the NSRecursiveLock
class to implement recursive locks instead.
Unlocking a lock that is not locked is considered a programmer error and should be fixed in your code. The NSLock
class reports such errors by printing an error message to the console when they occur.
@synchronized
directive is a convenient way to create mutex locks on the fly in Objective-C code. The @synchronized
directive does what any other mutex lock would do—it prevents different threads from acquiring the same lock at the same time. In this case, however, you do not have to create the mutex or lock object directly. The object passed to the @synchronized
directive is a unique identifier used to distinguish the protected block. If you execute the preceding method in two different threads, passing a different object for the anObj
parameter on each thread, each would take its lock and continue processing without being blocked by the other. If you pass the same object in both cases, however, one of the threads would acquire the lock first and the other would block until the first thread completed the critical section.
As a precautionary measure, the @synchronized
block implicitly adds an exception handler to the protected code. This handler automatically releases the mutex in the event that an exception is thrown. This means that in order to use the@synchronized
directive, you must also enable Objective-C exception handling in your code. If you do not want the additional overhead caused by the implicit exception handler, you should consider using the lock classes.
The class defines a lock that can be acquired multiple times by the same thread without causing the thread to deadlock. A recursive lock keeps track of how many times it was successfully acquired. Each successful acquisition of the lock must be balanced by a corresponding call to unlock the lock. Only when all of the lock and unlock calls are balanced is the lock actually released so that other threads can acquire it.
As its name implies, this type of lock is commonly used inside a recursive function to prevent the recursion from blocking the thread. You could similarly use it in the non-recursive case to call functions whose semantics demand that they also take the lock.
NSConditionLock
class defines objects whose locks can be associated with specific, user-defined conditions. Using an NSConditionLock
object, you can ensure that a thread can acquire a lock only if a certain condition is met. Once it has acquired the lock and executed the critical section of code, the thread can relinquish the lock and set the associated condition to something new. The conditions themselves are arbitrary: you define them as needed for your application. NSConditionLock
object when threads need to perform tasks in a specific order, such as when one thread produces data that another consumes. While the producer is executing, the consumer acquires the lock using a condition that is specific to your program. (The condition itself is just an integer value that you define.) When the producer finishes, it unlocks the lock and sets the lock condition to the appropriate integer value to wake the consumer thread, which then proceeds to process the data. The semantics for using an NSCondition
object are as follows:
-
Lock the condition object.
-
Test a boolean predicate. (This predicate is a boolean flag or other variable in your code that indicates whether it is safe to perform the task protected by the condition.)
-
If the boolean predicate is false, call the condition object’s
wait
orwaitUntilDate:
method to block the thread. Upon returning from these methods, go to step 2 to retest your boolean predicate. (Continue waiting and retesting the predicate until it is true.) -
If the boolean predicate is true, perform the task.
-
Optionally update any predicates (or signal any conditions) affected by your task.
-
When your task is done, unlock the condition object.