IOS multithreaded programming guide (two) thread management

Recommended for you: Get network issues from WhatsUp Gold. Not end users.

When the application creates a new thread, the thread into a real application process space. Each thread has its own stack, the running time by scheduling independent. A thread can and other threads or other communication process, perform a I/O operation, or perform any you want it to complete the task. Because they are in the same process space, so all the threads of a standalone application which share the same virtual memory space, and has the same access and process.


A cost, thread

Multi thread will occupy your application (and system) memory usage and performance in terms of resource. Each thread needs to allocate some kernel memory and application memory space memory. Manage your thread and coordinate its dispatching is the core data structure stored in the Wired Memory kernel. You the thread's stack space and each thread data is stored in your application's memory space. These data structure inside the most is when you first create a thread or process is created and initialized, they cost the cost is very high, because of the need and the kernel interaction.

Two,   create a thread

1,The use of NSThread

Use NSThread to create a thread can be two method:

  1. Use the detachNewThreadSelector:toTarget:withObject: method to create a new thread.
    [NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];

      //NSLog(@"%@", [NSThread currentThread]);


  2. Create a new NSThread object, and call its start method. (supported only in the iOS and Mac OS X v10.5 and later)
    NSThread* myThread = [[NSThread alloc] initWithTarget:self
                                            selector:@selector(myThreadMainMethod:)
                                            object:nil];
    [myThread start];  // Actually create the thread

    If you have a NSThread object, the threads that are currently running, you can give only the thread sends a message that any object in your application using the performSelector:onThread:withObject:waitUntilDone: method. In the Mac OS X v10.5 to support the implementation of selectors in multi thread (rather than in the main thread inside), and it is convenient for implementation method of thread communication. You use this technique to messages sent by other threads will be as the main body of the run-loop part of the direct execution. When you use this method to achieve the thread communication, you may still need a synchronous operation, but it set the communication port between threads is much easier than.

2,Multi thread using POSIX

Mac OS X and iOS provide the means to create thread POSIX thread API C language support based on. This technique can actually be any type of application to use (application including Cocoa and Cocoa Touch), and if you are really into application of multi platform development, the technology may be more convenient.

The following shows two custom function uses the POSIX to create a thread. The LaunchThread function creates a new thread, the thread routines are implemented by the PosixThreadMainRoutine function. Because the POSIX threads created default is connected (joinable), the following example changes the thread attribute to create a detached thread. The thread is marked as detachment, when it exits the system to have the opportunity to immediately recall the thread's resource.

#include  <assert.h>

#include  <pthread.h>

void* PosixThreadMainRoutine(void* data)
{
    // Do some work here.
    return NULL;
}

void LaunchThread()
{
    // Create the thread using POSIX routines.
    pthread_attr_t  attr;
    pthread_t       posixThreadID;
    int             returnVal;
    returnVal = pthread_attr_init(&attr);
    assert(!returnVal);
    returnVal = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    assert(!returnVal);
    int     threadError = pthread_create(&posixThreadID, &attr, &PosixThreadMainRoutine, NULL);
    returnVal = pthread_attr_destroy(&attr);
    assert(!returnVal);
    if (threadError != 0)
    {
         // Report an error.
    }
}

  If you put the above list add code to any of your source files, and call the LaunchThread function, create a new thread will be out it in your application. Of course, the newly created threads use the code does not do anything useful. Thread will load and exit immediately. In order to make it more interesting, you need to add code to the PosixThreadMainRoutine function to do some real work. In order to guarantee thread know what to do, you can create a pointer to the thread when a data transfer. The pointer as the last parameter pthread_create.


For the main thread communication in the new thread and inside of your application, you need to establish stable communication path between a and the target thread. For the application program based on C language, there are several ways to realize the communication between threads, including the use of port (ports), condition (conditions) and shared memory (shared memory). For the long-term presence of thread, you should almost always set up a communication mechanism between threads, so the main thread of your application has a way to check the state of the thread or clean the application exits when closing it.

More about the POSIX thread function information, refer to the pthread home page.



3,Use NSObject to create a thread

In the iOS and Mac OS X v10.5 and later, all the objects are likely to generate a new thread, and use it to perform its arbitrary. Methods performSelectorInBackground:withObject: new generation from a thread, using the specified method as the main entrance point of the new thread. For example, if you have some objects (use the variable myObj to represent), and these objects have the one you want to run in the background of the doSomething method, you can use the following code to generate a new thread:


[myObj performSelectorInBackground:@selector(doSomething) withObject:nil]; 

Calling this method and the effect of using NSThread in the current object inside the detachNewThreadSelector:toTarget:withObject: transmission of selectore, methods of object as a parameter. The new thread will be immediately generate and run, it uses the default settings. In selectore, you must configure the thread like you in any thread inside the same. For example, you may need to set up an auto release pool (if you are not using garbage collection mechanism), to use it when you configure the thread in run loop.

4, The use of other threads

Although the POSIX routine and the NSThread class is recommended to create the lower threads, but other C language technology based on Mac OS X is also available in the. In this one, the only one can consider the use of multi processing service (Multiprocessing Services), which itself is performed on the POSIX thread. Multi processing services specifically for the early Mac OS version developed by Mac OS, later in the X inside the Carbon application above also apply. If you have code really have this technology, you can continue to use it, although you should place this code into POSIX. The technology is not available in iOS.

More about how to use multi processing service information, see multi processing service programming guide(Multiprocessing Services Programming Guide).

5,The use of POSIX threads in the Cocoa program.

Although NSThread is the main interface of Cocoa application to create multi-threaded, if can be more convenient if you can bring any alternative to the use of the POSIX thread. For example, if your code has been used to it, and you don't want to rewrite it, then you may need to use the POSIX multi thread. If you really want to use POSIX thread in the Cocoa program, you should know if interaction in Cocoa and inter thread, some guidelines and follow the following parts.

  u  Protection of Cocoa framework

For the application of multi thread, the Cocoa framework uses locks and other synchronization to ensure correct execution of code. In order to protect these lock resulting in single threaded performance loss, Cocoa until the application uses the NSThread class to generate the first new thread of it to create these lock. If you only use POSIX routines and to generate a new thread, the Cocoa will not receive the application about your current notice into multiple threads. When these just happens, relates to Cocoa based operation oh may destroy even let your application crashes.

In order to let Cocoa know that you are planning to use multiple threads, you need to do is to use the NSThread class to create a thread, and let it immediately exit. The main entrance to do anything you don't need to thread. Only need to use NSThread to create a thread is enough to guarantee the required Cocoa frame lock in place.

If you are not sure whether Cocoa already know that your program is multi-threaded, isMultiThreaded you can use the NSThread method to check.

  u  The mixture of POSIX and Cocoa lock

The mixed use of POSIX and Cocoa in the same application inside the lock is safe. Cocoa lock and a condition object basically just package the POSIX mutexes and condition. 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 you use the pthread_mutex_init function to generate the mutex, and vice versa.



Three, configure the thread attribute

1,The size of the stack configuration thread

The new you create for each thread, the system will be in your process space allocated a certain amount of memory as the thread's stack. The stack management stack frame, as well as any thread local variable declarations. If the stack size you want to change a given thread, you must do something before creating the thread. All thread technology provides some measures to set the thread stack size. Although you can use NSThread to set the stack size, but it is only in the iOS and Mac OS X v10.5 and only available after the. Table 2-2 lists each technique for different operation.

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 the thread local storage

Each thread maintains a key value in the dictionary, it anywhere in the thread which can be accessed. You can use the dictionary to store some information, that information will remain unchanged during the execution of the thread. For example, you can use it to state information is stored in the thread in the course of your Run loop iterative.

Cocoa and POSIX in a different way to hold thread dictionary, so you can't mix up and at the same time the caller two technology. However, as long as you are in your thread code which insisted on using one technique, the final results should be the same. In Cocoa, the threadDictionary method you use NSThread to retrieve a NSMutableDictionary object, you can add any threads need to key in it. In POSIX, you use the pthread_setspecific and pthread_getspecific functions to set and access your thread keys and values.

3,Setting the thread from the state

Most of the upper thread technology are created by default from the thread(Datached thread). In most cases, (Detached thread) from the thread is more popular, because they allow the system to thread to complete the immediate release of the data structure. From the thread also do not need to display and your application interaction. Means that the thread retrieval results determined by you. In contrast, the system not recycled can be connected thread (Joinable thread) resources until another thread explicitly join the thread, this process may prevent thread executes the join.

You can think of is similar to the child thread connecting thread. Although you as an independent thread to run, but the can be connected to thread in which resources can be recovered before must be other thread connection. Can be connected to thread also provides a display way to pass data from one thread to another thread is out. In before it exits, connection thread can pass a pointer to data or other return a value to the pthread_exit function. Other threads can be through the pthread_join function to get the data.

Important: the application of in the program exit, away from the thread can be interrupted immediately, and the connecting thread can not. Each can be connected with thread must in the process of being allowed to exit is connected. So when the thread is in a period of work and does not allow the time interrupted, such as saving data to disk, connecting thread is the best choice.

If you want to create a thread can be connected, the only way is to use the POSIX thread. The POSIX default created thread is connected. In order to put the thread token away from or connected, use the pthread_attr_setdetachstate function to modify the attribute being created thread. In the thread is started, you can call the pthread_detach function to the thread modified to be connected. More about POSIX thread function information, participation in the pthread home page. More about if connected to a thread, refer to the pthread_join home page.

4,Setting the thread priority

Any thread the default priority you create your own thread is the same. The kernel scheduling algorithm in the decision of the operation of the thread, the thread priority as considerations, higher priority thread will be relatively low priority thread has a chance to run more. A higher priority thread execution does not guarantee that your time, but in comparison with the lower priority thread, it is more likely to be the scheduler selects execution.

Important: let your thread is in the default priority value is a good choice. Adding some thread priority, at the same time may increase the hunger for some low priority thread. If your application contains a high priority and low priority thread, and must be interaction between them, then the lower priority starvation may block other threads, and cause performance bottlenecks.

If you want to change the priority of the thread, Cocoa and POSIX provides a way to achieve. For Cocoa thread, the setThreadPriority: class you can use the NSThread method to set the currently running thread priority. For POSIX thread, you can use the pthread_setschedparam function to achieve. For more information, participate in the NSThread Class Reference or the pthread_setschedparam home page.



Four, the main entrance point to write your thread

For the most part, the main entrance point Mac OS X thread structure and other platforms. You need a data structure initialization you, do some work or feasible to set up a run loop, and the thread code is executed after cleaning it. According to the design, when you write the main entrance point may need to take a few extra steps.

1,Create an auto release pool

In the Objective – C framework application link, usually in each thread they must create at least one auto release pool. If the application uses a management model, namely the application handles retain and release objects, so the auto release pool capture any from the autorelease thread object.

If the application uses the garbage collection mechanism, rather than managing memory model, then create an auto release pool is not absolutely necessary. In the application of garbage collection, an auto release pool is harmless, but mostly ignored. Allow the code management must support both garbage collection and memory management model. In this case, the memory management model must support auto release pool, when the application runs garbage collection, auto release pool just be ignored.

If your application uses memory management model, to create the first thing when you write thread main entrance is an auto release pool. Similarly, in your thread finally should destroy the auto release pool. The pool ensures the automatic release. Although the object is invoked, but they are not release until the thread exits.

- (void)myThreadMainRoutine
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
    // Do thread work here.
    [pool release];  // Release the objects in the pool.
}

Because the senior auto release pool won't release it until the thread exits the object. The running thread needs new auto release pool extra to more frequent releases the object length. For example, a run loop thread may be in each run once cycle time to create and release the auto release pool. The object is released more frequently can prevent your application memory occupies too large causing performance problems. Although for any performance related behavior, you should be the actual performance measurements of your code, and appropriately adjusted using an auto release pool.

More about memory management information and auto release pool, see “ memory management programming guide(Advanced Memory Management Programming Guide)”.

2,Set the exception handling

If your application is to capture and handle exceptions, then your thread code should always be prepared to catch the exception of any possible. Although the best way is in abnormal place of capture and deal with it, but if your thread inside catch an exception failed throw words can cause your application to strong back. Install a try/catch module in the main entrance point of your thread, allows you to capture any abnormal of the unknown, and to provide an appropriate response.

When building your project in Xcode, exception handling style, you can use the C++ or Objective-C. More about setting to throw and catch exceptions in Objective-C, see Exception Programming Topics.

3,Set up a Run Loop

When you want to write a separate thread to run, you have two choices. The first option is to write the code as a long-term task, with little or no interruption, when threads to complete exit. The second option is to put your thread into a loop inside, make it dynamic arrival task request. The first method does not need to specify any things in your code; you just need to start doing what you want to do. However, second kinds of choices need to add a run loop in your thread.

Mac OS X and iOS run loop provides built-in support for each thread. Cocoa, Carbon and UIKit automatically in the main thread of your application to start a run loop, but if you create any auxiliary thread, you must manually set up a run loop and start it.

4, Interrupt threads

Method of exiting a thread recommendation is to keep it in its main entrance point normal exit. Although Cocoa, POSIX and Multiprocessing Services provides direct kill thread routines, but the use of these routines is strongly discouraged. Kill a thread blocked thread itself cleanup. Thread the allocated memory may cause leakage, and other threads that are currently using resources may not be properly cleaned, then cause potential problems.

If your application needs to interrupt a thread is in the middle of an operation, you should design your thread responds to cancel or exit message. For a long time operation, which means that periodically stop working to check whether the message arrival. If the message is coming and the thread exits, then the thread will have the opportunity to perform any clean-up and exit; otherwise, it returns to work and deal with the next data block.

A method to cancel the response message is input source using run loop to receive these messages. The following shows which is similar to the structure of the main entrance of code in your thread is like (this example shows the main circulation part, not including the establishment of an auto release pool or configure the actual work steps). The sample provided one input source has been defined in the run loop above, it can receive messages from other threads. . A partial sum of executive working, threads run run loop to see if there is a message arrives at the input source. If not, the run loop exit immediately, and the cycle continues processing the next data block. Because the processor has no access to the local variable exitNow directly, the exit condition is transmitted through thread dictionary.

- (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];
    }
}

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Posted by Kaye at November 28, 2013 - 9:31 AM