When an application generates a new thread, the thread becomes an entity in the application process space. Each thread has its own execution stack, which is run independently by the kernel scheduling. A thread can communicate with other threads or other processes, execute I/O operations, or even execute any task you want it to complete. Because they are in the same process space, all threads in an independent application share the same virtual memory space and have the same access permissions as the process.
I. Thread costs
Multithreading occupies the memory usage and performance resources of your applications (and systems. Each thread needs to allocate a certain amount of kernel memory and application memory space. The core data structures required to manage your threads and coordinate their scheduling are stored in the kernel using Wired Memory. The stack space of your thread and the data of each thread are stored in the memory space of your application. Most of these data structures are created and initialized when you create a thread or process for the first time. They are costly because they need to interact with the kernel.
2. Create a thread
1. Use NSThread
There are two possible methods to use NSThread to create a thread:
2. Use POSIX Multithreading
Mac OS X and iOS provide methods to create threads using POSIX thread APIs Based on C language support. This technology can actually be used by any type of applications (including Cocoa and Cocoa Touch applications), and it may be more convenient if you are actually developing applications for multiple platforms.
The following shows two user-defined functions that use POSIX to create threads. The LaunchThread function creates a new thread. The thread routine is implemented by the PosixThreadMainRoutine function. Because the threads created by POSIX are joinable by default, the following example changes the attributes of the threads to create an detached thread. Mark the thread as disconnected. When it exits, the system immediately recycles the resources of the thread.
#include <assert.h><pthread.h>
* PosixThreadMainRoutine(*
= pthread_attr_init(&!= pthread_attr_setdetachstate(&! threadError = pthread_create(&posixThreadID, &attr, &= pthread_attr_destroy(&! (threadError !=
If you add the code in the above list to any of your source files and call the LaunchThread function, it will create a new detached thread in your application. Of course, the newly created thread uses this code without doing anything useful. The thread is loaded and exits immediately. To make it more interesting, you need to add code to the PosixThreadMainRoutine function to do some practical work. To ensure that the thread knows what to do, you can pass a data pointer to the thread during creation. Use this pointer as the last parameter of pthread_create.
To communicate with the main thread of your application in the newly created thread, you need to establish a stable communication path between the thread and the target thread. For C-language-based applications, there are several ways to implement inter-thread communication, including using ports, conditions, and shared memory ). For long-standing threads, You Should almost always set up a mechanism for inter-thread communication, let your application's main thread have a way to check the thread status or close it cleanly when the application exits.
For more information about POSIX thread functions, see the pthread homepage.
3. Use NSObject to generate a thread
In iOS and Mac OS X v10.5 and later versions, all objects may generate a new thread and use it to execute arbitrary methods. Method msmselectorinbackground: withObject: generates a new detached thread and uses the specified method as the main entry point of the new thread. For example, if you have some objects (represented by the variable myObj) and these objects have a doSomething method that you want to run in the background, you can use the following code to generate a new thread:
[myObj performSelectorInBackground:@selector(doSomething) withObject:nil];
The effect of calling this method is the same as that of using detachNewThreadSelector: toTarget: withObject: To pass selectore in the current object. The new thread will be generated and run immediately. It uses the default settings. Inside selectore, you must configure the thread as you do in any thread. For example, you may need to set an automatic release pool (if you do not use the garbage collection mechanism) and configure the thread's run loop when you want to use it.
4. Use other thread Technologies
Although POSIX routines and NSThread classes are recommended to create low-level threads, other C-language-based technologies are also available on Mac OS X. Among them, the only one that can be considered is the Multiprocessing Services, which itself is executed on the POSIX thread. The multi-processing service was developed for earlier Mac OS versions. Later, it was applied to the Carbon application in Mac OS X. If you have code that really has this technology, you can continue to use it, although you should convert the code into POSIX. This technology is unavailable on iOS.
For more information about how to use the multi-processing service, seeMultiprocessing Services Programming Guide).
5. Use POSIX Threads on the Cocoa Program
The NSThread class is the main interface used to create multiple threads in the Cocoa application. If it is more convenient, you can use POSIX Threads instead. For example, if your code already uses it and you don't want to rewrite it, you may need to use POSIX multithreading. If you really want to use POSIX Threads in the Cocoa program, you should understand if Cocoa interacts with threads and follow the instructions in the following sections.
Protection of the u Cocoa framework
For multi-threaded applications, the Cocoa framework uses locks and other Synchronization Methods to ensure correct code execution. To protect these locks from performance loss in a single thread, Cocoa creates these locks until the application uses the NSThread class to generate its first new thread. If you only use POSIX routines to generate new threads, Cocoa will not receive notifications about how your applications are currently becoming multithreading. When this happens, operations involving the Cocoa framework may destroy or even crash your application.
To let Cocoa know that you are planning to use multithreading, you need to use the NSThread class to generate a thread and immediately exit it. The main entry point of your thread does not need to do anything. You only need to use NSThread to generate a thread to ensure that the locks required by the Cocoa framework are in place.
If you are not sure whether Cocoa knows that your program is multi-threaded, you can use the isMultiThreaded method of NSThread to check whether it is known.
U mixed POSIX and Cocoa locks
It is safe to mix POSIX and Cocoa locks in the same application. The Cocoa lock and condition object basically encapsulate the mutex and condition of POSIX. However, given a lock, you must always use the same interface to create and manipulate the lock. In other words, you cannot use the Cocoa NSLock object to manipulate a mutex generated by using the pthread_mutex_init function, and vice versa.
3. Configure thread attributes
1. Configure the thread stack size
For each newly created thread, the system allocates a certain amount of memory in your process space as the stack of the thread. The stack management stack frame is also the place where local variables of any thread are declared. If you want to change the stack size of a given thread, you must perform some operations before creating the thread. All thread technologies provide some methods to set the thread stack size. Although you can use NSThread to set the stack size, it is only available for iOS and Mac OS X v10.5 and later. Table 2-2 lists different operations for each technology.
Technology |
Option |
Cocoa |
In iOS and Mac OS X v10.5 and later, allocate and initialize an NSThread object (do not use thedetachNewThreadSelector: toTarget: withObject: method ). before calling the start method of the thread object, use thesetStackSize: method to specify the new stack size. |
POSIX |
Create a new pthread_attr_t structure and use the pthread_attr_setstacksize function to change the default stack size. Pass the attributes to the pthread_create function when creating your thread. |
Multiprocessing Services |
Pass the appropriate stack size value to the MPCreateTask function when you create your thread. |
2. Configure local thread storage
Each thread maintains a key-value dictionary, which can be accessed anywhere in the thread. You can use this dictionary to save some information, which remains unchanged throughout the thread's execution process. For example, you can use it to store the status information of multiple iterations in the Run loop of your entire thread process.
Cocoa and POSIX store the dictionary of threads in different ways, so you cannot confuse and call both technologies at the same time. However, as long as you stick to one of the technologies in your thread code, the final result should be the same. In Cocoa, you use the threadDictionary method of NSThread to retrieve an NSMutableDictionary object. You can add the keys required by any thread in it. In POSIX, you use the pthread_setspecific and pthread_getspecific functions to set and access the keys and values of your threads.
3. Set the thread disconnection status
Most upper-layer thread technologies are created by default.Remove from thread(Datached thread ). In most cases,Remove from thread(Detached thread) is more popular because they allow the system to immediately release its data structure when the thread completes.Remove from threadYou do not need to display interaction with your application. This means that the result of the thread search is determined by you. In contrast, the system does not recycleConnectable threads(Joinable thread) resources until another thread explicitly joins the thread, this process may prevent the thread from being added.
You can thinkConnectable threadsSimilar to a subthread. Although you are running as an independent threadConnectable threadsIt must be connected by other threads before its resources can be recycled by the system.Connectable threadsIt also provides a display method to pass data from an exiting thread to another thread. Before exiting,Connectable threadsYou can pass a Data Pointer or other returned values to the pthread_exit function. Other threads can obtain the data through the pthread_join function.
Important:When an application exits, it can be interrupted immediately after it leaves the thread, but cannot be connected to a thread. Each connectable thread must be connected when the process is allowed to exit. Therefore, when the thread is working cyclically and cannot be interrupted, such as saving data to the hard disk, it is the best choice to connect to the thread.
If you want to create a connectable thread, the only way is to use the POSIX thread. By default, the threads created by POSIX are connectable. To mark the thread as disconnected or connectable, use the pthread_attr_setdetachstate function to modify the attributes of the thread being created. After the thread starts, you can call the pthread_detach function to change the thread to connectable. For more POSIX thread functions, go to the pthread homepage. For more information about connecting to a thread, see the pthread_join homepage.
4. Set the thread priority.
The default priority of any thread you create is the same as that of your own thread. When the kernel scheduling algorithm determines the thread to run, the priority of the thread is taken as a consideration. A thread with a higher priority has more running opportunities. A higher priority does not guarantee the specific execution time of your thread. It is more likely to be selected by the Scheduler for execution than a lower priority thread.
Important:Setting your thread to the default priority is a good choice. Increase the priority of some threads and possibly increase the hunger of some lower-priority threads. If your applications contain high-priority and low-priority threads that must interact with each other, hunger with a lower-priority State may block other threads and cause performance bottlenecks.
If you want to change the thread priority, both Cocoa and POSIX provide a method for implementation. For Cocoa threads, you can use NSThread's setThreadPriority: class method to set the priority of the currently running thread. For POSIX Threads, you can use the pthread_setschedparam function. For more information, visit the NSThread Class Reference or pthread_setschedparam homepage.
4. Compile the main point of your thread
For most cases, the main entry points of the thread structure on Mac OS X are basically the same as those on other platforms. You need to initialize your data structure, do some work or set a run loop, and clear it after the thread code is executed. According to the design, you may need to take some additional steps when writing the subject entry point.
1. Create an automatic release pool
Applications linked to the Objective-C framework usually have to create at least one Auto Release pool in each of their threads. If the application uses a management model, that is, the retain and release objects processed by the application, the pool is automatically released to capture any objects from the thread autorelease.
If the application uses a garbage collection mechanism instead of a managed memory model, creating an automatic release pool is not absolutely necessary. In a garbage collection application, an automatic release pool is harmless and is ignored in most cases. Code Management must support both garbage collection and memory management models. In this case, the memory management model must support automatic release of the pool. When the application is running garbage collection, the automatic release of the pool is ignored.
If your application uses the memory management model, the first thing you need to do when writing the thread body entry is to create an automatic release pool. Similarly, the auto release pool should be destroyed at the end of your thread. The pool is automatically released. Although objects are called, they are not release until the thread exits.
- (*pool = [[NSAutoreleasePool alloc] init];
[pool release];
}
Because the advanced automatic release pool does not release its objects until the thread exits. Long-running threads need to create an additional automatic release pool to release its objects more frequently. For example, a thread using the run loop may create and release the automatic release pool after each cycle is completed. Releasing objects more frequently can prevent performance problems caused by high memory usage of your applications. Although for any behavior related to performance, you should measure the actual performance of your code and adjust the use of Auto Release pools as appropriate.
For more information about Memory Management and automatic release pools, see Advanced Memory Management Programming Guide )".
2. Set Exception Handling
If your application captures and handles exceptions, your thread code should always be ready to capture any possible exceptions. Although the best way is to capture and handle exceptions where they occur, if you capture a thrown exception failure in your thread, it may cause your application to crash. Installing a try/catch module at the main entry point of your thread allows you to capture any unknown exceptions and provide a suitable response.
When building your project in Xcode, you can use the exception handling style of C ++ or Objective-C. For more information about how to throw and capture exceptions in Objective-C, see Exception Programming Topics.
3. Set a Run Loop
When you want to write a separate running thread, you have two options. The first option is to write code as a long-term task with little or no interruptions and exit when the thread completes. The second option is to put your thread into a loop and let it dynamically process the incoming task requests. The first method does not need to specify anything in your code; you only need to do what you plan to do at startup. However, the second option requires you to add a run loop in your thread.
Mac OS X and iOS provide built-in support for running loop in each thread. Cocoa, Carbon, and UIKit automatically start a run loop in the main thread of your application, but if you create any auxiliary thread, you must manually set a run loop and start it.
4. Thread interruption
The recommended way to exit a thread is to let it exit normally at its main entry point. Cocoa, POSIX, and Multiprocessing Services provide routines for directly killing threads, but these routines are strongly discouraged. Killing a thread stops the cleanup of the thread itself. The memory allocated by the thread may cause leakage, and the resources currently used by other threads may not be properly cleaned up, resulting in potential problems.
If your application needs to interrupt a thread in the middle of an operation, you should design your thread to respond to the canceled or exited message. For long-running operations, this means that the work is stopped periodically to check whether the message has arrived. If the message does come and requires the thread to exit, the thread will have the opportunity to execute any cleanup and logout work; otherwise, it will return to continue working and process the next data block.
One way to respond to a cancellation message is to use the run loop Input Source to receive the message. The following shows the code similar to this structure in the main entry of your thread (This example shows the main loop section, does not include setting up an automatic release pool or configuring the actual working steps ). In this example, a custom Input Source is installed on the run loop, which can receive messages from other threads .. After a part of the total execution, the thread runs the run loop to check whether messages arrive at the input source. If no, run loop immediately exits and continues processing the next data block. Because the processor does not directly access the exitNow local variable, the exit condition is transmitted through the thread dictionary.
- (==* runLoop =
NSMutableDictionary* threadDict =
(moreWorkToDo && !
exitNow = [[threadDict valueForKey: