iOS multithreaded Programming Guide (ii) thread management

Source: Internet
Author: User
Tags posix



When the application generates a new thread, the thread becomes an entity within the application process space. Each thread has its own execution stack, with the kernel dispatching a separate run time slice. A thread can communicate with other threads or other processes, perform I/O operations, and even perform any task that you want it to accomplish. Because they are in the same process space, all threads within a standalone application share the same virtual memory space and have the same access rights as the process.






One, the cost of the thread



Multithreading consumes the memory usage and performance resources of your application (and the system). Each thread needs to allocate memory for a certain amount of kernel memory and application memory space. The core data structures needed to manage your threads and coordinate their scheduling are stored in the kernel using wired memory. Your thread's stack space and the data for each thread are stored in the memory space of your application. Most of these data structures are created and initialized when you first create a thread or process, and they cost a lot of money because you need to interact with the kernel.



Second, create a thread



1. Using Nsthread



There are two possible ways to create a thread using Nsthread:


    The
    1. uses the DetachNewThreadSelector:toTarget:withObject: class method to generate a new thread.
       [Nsthread detachnewthreadselector: @selector (mythreadmainmethod:) totarget:self Withobject:nil]; 

       //nslog (@ "%@"

       

    2. The
    3. creates a new Nsthread object and calls its Start method. (supported only on iOS and Mac OS X v10.5 and beyond)
      
      
      NSThread* myThread = [[NSThread alloc] initWithTarget:self
                                              selector:@selector(myThreadMainMethod:)
                                              object:nil];
      [myThread start];  // Actually create the thread

      If you have a Nsthread object whose thread is currently running, the only way you can send messages to that thread is to use any object inside your application Performselector : OnThread:withObject:waitUntilDone: Method. The Mac OS X v10.5 supports the execution of selectors on multiple threads (rather than the main thread), and it is a convenient way to implement inter-thread communication. The messages you send when you use the technology are executed directly by other threads as part of the Run-loop body. When you use this method to implement thread communication, you may still need a synchronous operation, but this is much simpler than setting up communication ports between threads.


2. Multithreading with POSIX



Mac OS x and iOS provide a way to use the POSIX threading API to create threads based on the C language support. The technology can actually be used by any type of application (including cocoa and cocoa touch applications) and may be more convenient if you are currently developing applications for multiple platforms.



The following shows two custom functions that use POSIX to create a thread. The Launchthread function creates a new thread, and the thread's routines are implemented by the Posixthreadmainroutine function. Because POSIX-created threads are joinable by default, the following example alters the thread's properties to create a disconnected thread. Mark the thread as detached, and let the system have the opportunity to reclaim the thread's resources immediately when it exits.




#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 add the code from the above list to any of your source files, and call the Launchthread function, it will create a new out-of-thread in your application. Of course, the newly created thread doesn't do anything useful with that code. The thread will load and exit immediately. To make it more interesting, you need to add code to the Posixthreadmainroutine function to do some real work. To make sure the thread knows what to do, you can pass a pointer to the thread at the time of creation. Use the pointer as the last parameter of the pthread_create.






In order to communicate with your application's main thread inside the new threads, you need to establish a stable communication path between the target threads. For C-based applications, there are several ways to communicate between threads, including the use of ports (ports), conditions (conditions), and shared memory. For a long-standing thread, you should almost always set up a communication mechanism between the threads so that the main thread of your application has a way to check the state of the thread or to cleanly close it when the application exits.



For more information about POSIX threading functions, see the home page of Pthread.









3. Use NSObject to create a thread



In iOS and Mac OS X v10.5 and beyond, all objects can generate a new thread and use it to execute arbitrary methods. Method Performselectorinbackground:withobject: The newborn becomes a detached thread, using the specified method as the principal entry point for the new thread. For example, if you have some objects (represented using 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:


The effect of calling this method is the same as the detachNewThreadSelector:toTarget:withObject that you use Nsthread in the current object: passing the Selectore,object as a parameter. The new thread will be generated and run immediately, using the default settings. Inside Selectore, you have to configure the thread just as you would in any thread. For example, you might need to set up an auto-release pool (if you're not using a garbage collection mechanism) and configure the thread's run loop when you want to use it.



4. Using other threading Technologies



Although POSIX routines and Nsthread classes are recommended to create low-level threads, other C-based technologies are also available on Mac OS X. In this, the only one that can be considered is the multi-processing service (multiprocessing services), which itself is executed on a POSIX thread. The multi-processing service was developed specifically for earlier versions of Mac OS, and later on in Mac OS X carbon applications. If you have code that really has the technology, you can continue to use it, even though you should turn the code into POSIX. This technology is not available on iOS.



For more information on how to use the multi-processing service, refer to the multi-processing Services Programming Guide (multiprocessing Services programming guides).



5. Using POSIX threads on cocoa programs



The Nsthread class is the main interface for creating multithreading in the Cocoa application, and you can use POSIX thread substitution if it is more convenient. For example, if you have used it in your code and you don't want to rewrite it, you might want to use POSIX multithreading. If you really intend to use POSIX threads in the cocoa program, you should understand that if you interact between cocoa and threads, follow some of the guidelines below.



Protection of U cocoa Frame



For multithreaded applications, the cocoa framework uses locks and other synchronization methods to ensure the correct execution of the code. To protect these locks from the loss of performance within a single thread, cocoa creates these locks until the application uses the Nsthread class to generate its first new thread. If you only use the POSIX routines to generate new threads, cocoa will not receive notifications about your application becoming multi-threaded. When this happens, the operations involved in the cocoa framework may break even your application.



In order for cocoa to know that you are planning to use multi-threading, all you need to do is generate a thread using the Nsthread class and let it exit immediately. The main entry point of your thread does not need to do anything. Just using Nsthread to generate a thread is enough to ensure that the cocoa framework requires a lock in place.



If you're not sure if cocoa already know your program is multithreaded, you can use Nsthread's ismultithreaded method to test it.



U-mix posix and cocoa locks



It is safe to mix posix and cocoa locks in the same application. Cocoa locks and conditional objects basically just encapsulate POSIX mutexes and conditions. Given a lock, however, you must always use the same interface to create and manipulate the lock. In other words, you cannot use Cocoa's Nslock object to manipulate a mutex that you generate using the Pthread_mutex_init function, and vice versa.









Third, configure thread properties



1. Configure the thread's stack size



For each new thread you create, the system allocates a certain amount of memory within your process space as the thread's stack. The stack manages the stack frame and is also where any thread local variable is declared. If you want to change the stack size of a given thread, you must do something before you create the thread. All threading techniques provide some way to set the size of the thread stack. Although you can use Nsthread to set the stack size, it is available only on iOS and Mac OS X v10.5 and beyond. Table 2-2 lists the different operations for each of these technologies.


Technology

Option

Cocoa

In IOS and Mac OS X v10.5 and later, allocate and initialize a Nsthread object (do not use Thedetachnewthreadselector:tot Arget:withObject:method). Before calling the Start method of the 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 if you create your thread.


2. Configuring Thread Local Storage



Each thread maintains a dictionary of key-values that can be accessed anywhere inside the threads. You can use this dictionary to hold some information that remains the same throughout the execution of the thread. For example, you can use it to store state information for multiple iterations of the run loop during your entire thread.



Cocoa and POSIX save the thread's dictionary in different ways, so you can't confuse and simultaneously invoke both techniques. However, as long as you persist in using one of the techniques within your thread code, the final result should be the same. In cocoa, you use Nsthread's Threaddictionary method to retrieve a Nsmutabledictionary object that you can add to any thread you want. Inside POSIX, you use the pthread_setspecific and Pthread_getspecific functions to set and access the keys and values of your thread.



3. Set the detachment state of the thread



Most of the upper-level threading technology creates the datached thread by default. In most cases, the Detached thread is more popular because they allow the system to release its data structure as soon as it is finished. The detach thread does not need to display the interaction with your application at the same time. means that the results of the thread retrieval are up to you. In contrast, the system does not reclaim the resources of the joinable thread until another thread explicitly joins the thread, and this process may prevent the thread from executing the join.



You can think of a thread that can connect like a child thread. Although you are running as a standalone thread, a threaded connection thread must be connected by another thread before it can be reclaimed by the system. A threaded thread provides a way to pass data from one exiting thread to another. Before it exits, the threaded connector can pass a data pointer or other return value to the Pthread_exit function. Other threads can use the Pthread_join function to get the data.



Important: when an application exits, the disengagement thread can be interrupted immediately, while the thread that can connect is not. Each connected thread must be connected when the process is allowed to exit. So when a thread is working periodically and not allowed to be interrupted, such as saving data to a hard disk, a thread that can be connected is the best choice.



If you want to create a threaded connection, the only way to do this is to use a POSIX thread. POSIX threads created by default are available for connection. To mark a thread as detached or to be connected, use the Pthread_attr_setdetachstate function to modify the properties of the thread that is being created. After the thread is started, you can modify the threads to be connected by calling the Pthread_detach function. For more information about POSIX thread functions, participate in the Pthread home page. About more if you are connecting to a thread, see Pthread_join's home page.



4, set the priority of the thread



The default priority for any thread you create is the same as your own thread. When the kernel scheduling algorithm determines which thread to run, the priority of the thread is taken as a consideration, and the higher-priority thread has more running opportunities than the lower-priority thread. A higher priority does not guarantee that your thread will execute exactly as long as it does, but it is more likely to be executed by the scheduler than a lower-priority thread.



Important: having your thread in the default priority value is a good choice. Increases the priority of some threads, while potentially increasing the hunger level of some lower-priority threads. If your application contains higher-priority and lower-priority threads, and they must interact with each other, a lower-priority hunger State can block other threads and cause performance bottlenecks.



If you want to change the priority of a thread, both cocoa and POSIX provide a way to implement it. For cocoa threads, you can use the Nsthread SetThreadPriority: Class method to set the priority of the currently running thread. For POSIX threads, you can use the Pthread_setschedparam function to implement. For more information, participate in the Nsthread Class reference or Pthread_setschedparam home page.









Iv. writing the main entry point of your thread



For the most part, Mac OS x is basically the same as the main entry point for the thread structure above the other platforms. You need to initialize your data structure, do some work or set up a run loop, and clean it up after the thread code is executed. By design, you may need to take some extra steps when writing the main entry point.



1. Create an auto-release pool



In the OBJECTIVE–C framework, applications that are linked to each other typically must create at least one auto-free pool on each of their threads. If an application uses the administrative model, which is the retain and release objects that the application processes, then the auto-free pool captures any objects that are autorelease from that thread.



If an application uses a garbage collection mechanism rather than a managed memory model, it is not absolutely necessary to create an auto-free pool. In a garbage-collected application, an auto-free pool is harmless, and most of the cases are ignored. Allowing for code management requires both a garbage collection and a memory management model to be supported. In this case, the memory management model must support the auto-free pool, which is simply ignored when the application runs garbage collection.



If your application uses a memory management model, the first thing you do when you write the thread principal entry is to create an auto-free pool. Again, the automatic release pool should be destroyed at the end of your thread. The pool is guaranteed to be released automatically. Although objects are called, 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 an advanced auto-release pool does not release its objects until the thread exits. Long-running threads require new additional auto-release pools to release its objects more frequently. For example, a thread that uses run loop might create and release the auto-free pool each time a loop is run. Releasing objects more frequently can prevent your application from using too much memory to cause performance problems. While for any performance-related behavior, you should measure the actual performance of your code and adjust the use of the auto-release pool appropriately.



For more information on memory management and auto-release pools, refer to the Memory Advanced Management Programming Guide (Advance memory Management programming guides).



2. Set exception handling



If your application catches and handles exceptions, then your thread code should always be ready to catch any exceptions that might occur. While it is best to capture and process the exception where it happened, it is possible for your application to retire if it fails to catch a thrown exception in your thread. Installing a Try/catch module at the main entry point of your thread allows you to catch any unknown exception and provide a suitable response.



When you build your project in Xcode, you can use the exception-handling style of C + + or objective-c. For more information on how to throw and catch exceptions in Objective-c, see Exception Programming Topics.



3. Set up a run Loop



When you want to write a thread that runs independently, you have two options. The first option is to write code as a long-term task, with little or no interruption, when the thread completes the exit. The second option is to put your thread in a loop and let it handle incoming task requests on the fly. The first method does not need to specify anything in your code; you just need to do what you want to do when you start. The second option, however, is to add a run loop to your thread.



Mac OS x and iOS provide built-in support for implementing run loops on each thread. Cocoa, carbon, and Uikit automatically start a run loop on the main thread of your application, but if you create any worker threads, you must manually set up a run loop and start it.



4, in the disconnection process



The recommended way to exit a thread is to have it exit normally at its main entry point. Cocoa, POSIX, and multiprocessing services provide routines that kill threads directly, but using these routines is strongly discouraged. Killing a thread prevents cleanup of the thread itself. Thread-allocated memory can cause a leak, and the resources currently used by other threads may not be properly cleaned up, causing potential problems later.



If your application needs to break a thread in the middle of an operation, you should design your thread to respond to the cancellation or exit message. For long-running operations, this means periodically stopping work to check whether the message arrives. If the message does come and requires the thread to exit, then the thread has the opportunity to perform any cleanup and exit, otherwise it returns to work and process the next block of data.



One way to respond to a cancellation message is to use the input source of the run loop to receive these messages. The following shows how similar code for this structure is in the main entrance of your thread (the example shows the main loop section, excluding the setting up of an auto-release pool or configuring the actual work steps). The example installs a custom input source above the run loop that can receive messages from other threads ... After performing a portion of the sum of work, the thread runs the run loop to see if a message arrives at the input source. If not, the run loop exits immediately, and the loop continues to process the next block of data. Because the processor does not have direct access to the Exitnow local variable, the exit condition is transmitted through the thread's 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];
    }
}


iOS multithreaded Programming Guide (ii) thread management


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.