线程编程指南 Threading Programming Guide (2)[苹果官方文档的翻译]

关于线程编程

     很多年来,计算机单微处理器的速度大大限制了计算机的性能。个人处理器性能达到极限时,芯片制造商转向了多核设计,计算机因此可以同时处理多个任务。OS X在执行系统相关的任务时充分利用了多核,你自己的应用也可以通过线程这么做。

什么是线程呢?

       线程是应用内实现多个执行路径的轻量级方法。在系统级别,程序并排地运行,系统根据每个程序的需求分配执行时间。在每个应用内容,可能一个或者多个执行线程,这样可以同步地或者以类似同步的方式执行不同的任务。实际上,系统本身管理着这些执行线程,安排他们运行可用的内核,或者俺需要打断他们允许其他线程运行。

      从技术角度讲,一个线程结合了内核级以及应用级的数据结构来管理代码的执行。内核级的结构协调派发给线程的事件以及线程的优先级调度。应用级的结构包括存储函数调用的栈,以及管理和操作线程属性和状态的结构。

      在非并发的应用中,只有一个执行线程。这个线程随着main 程序 开始和结束。并且一个接一个地到不同的方法或者函数中实现应用的所有行为。相比之下,支持并发的应用以一个线程开始,并且可以根据需要创建额外的执行路径。每一个新的路径都有它自己的开始程序在应用的主程序中运行代码。应用中的多线程提供了两个很重要的潜在优势

1,多线程可以提高应用的响应能力

2,多线程可以提高多核系统的实时性能

       如果你的应用只有一个线程,那么这个线程就得做一切。它必须响应事件,更新应用得窗口,执行所有计算去实现应用功能。仅仅使用一个线程的问题是同一刻它只能做一件事。那么假如你的某个计算任务需要花很久才能完成呢? 你的应用忙于计算,而应用停止了用户响应,无法更新窗口。如果这样持续了很久,用户会认为你的应用不工作了,然后强制退出。如果你把这些计算移到一个单独的线程,这样就可以即时响应用户操作了。

       随着多核计算机的流行,线程可以增加某些应用的性能。线程可以在不同的处理器内核上同步执行不同的任务,这样应用可以提高速度。

       当然了,线程并不是解决应用性能问题的万能药。线程的优点也伴随着潜在的问题。 使用多线程会让代码比较复杂度。每个线程都必须和其他线程配合防止弄错应用的状态信息。因为在单一应用中,线程共享相同的内存空间,它们访问所有相同的数据结构。如果两个线程想要同时操作相同的数据,这样会导致数据错乱。尽管有着保护,但你还是要小心编译器优化代理的显式或者隐式的错误。

      尽管线程核心实现机制是Mach 线程,但是你很少使用。 相反,你经常使用更方便的 POSIX API或者它的衍生技术。


      下面的列出了你可以使用的线程技术

1,Cocoa 使用NSThread类实现线程。Cocoa也提供NSObject的方法生产新的线程。可以参考 “Using NSThread” and “Using NSObject to Spawn a Thread.”

2,POSIX线程提供了基于C的接口来创建线程。如果你写的不是Cocoa程序,这是最好的选择。可以参考“Using POSIX Threads”


        在应用程序级别,所有的线程行为本质上是一样的。一个线程启动后,线程以三种主要状态(runing,ready 后者blocked)之一运行着。如果一个线程不在runing状态,那它要么是被阻塞等待输入要么是装备是运行但是还没开始。线程继续在这些状态中来回切换,直到最后退出,变成终止状态。

       当你创建一个新的线程,你必须为线程指定一个入口点函数。这个函数构建你想要运行在这个线程的代码。当函数返回时,或者当你终结线程时,线程被永远的停止,并且被系统回收。因为考虑到内存和时间,创建线程代价相对不小,所以你的入口点函数要做一些重要的工作,或者建立一个run loop 运行执行。


对于更多线程相关的技术,你可以参考“Thread Management.”


Run Loops


       Run loop 是管理异步到达线程事件的基础设施。run loop 监控线程的一个或者多个事件源。事件到达时,系统唤醒线程,派发事件给run loop,run loop然后派发事件给你指定的处理器。如果没有事件要处理,那么run loop会让线程睡眠。

       并不强制为每个线程使用run loop,但是使用了可以提供更好的用户体验。Run loop可以用最少的资源创建生存期长的线程。由于没什么需要做时run loop会让线程睡眠,它消除了轮询。


      配置一个run loop ,你所要做的只是开启你的线程,引用run loop 对象,安装你的事件处理器,然后让run loop 运行。OS X为你自动配置了主线程的run loop。如果是你自己的线程,你要自己配置run loop.你可以参考“Run Loops.”

Details about run loops and examples of how to use them are provided in “Run Loops.”

Synchronization Tools


线程编程的危险之一就是多线程之间的资源争夺。如果多个线程想要同时使用或者更改资源,糟糕的问题就会出现。其中一种缓解问题的办法是排除共享资源。确保每个线程在不同的资源集合上操作。如果这不行,那么你必须使用lock ,条件,原子操作或者其他技术来同步访问资源。


锁提供了保护代码的粗暴方式,那就是一次只能有一个线程执行。最常见的锁是可变排斥锁,或者叫mutex. 当一个线程想要获取被其他线程保持的锁,它就会阻塞直到锁被释放。一些系统框架提供对mutex锁的支持,尽管它们都是基于同样的底层技术。另外,Cocoa还提供mutex 锁一些变量以支持不同的行为,比如循环。可以参考 “Locks.”


除了锁,系统还提供了条件,这会保证应用内的任务按顺序执行。一个condition就像一个门卫,阻塞一个给定的线程直到condition为真。为真时,condition就会释放线程,允许它继续执行。POSIX层和Foundation框架都对condition提供了直接的支持。(如果你使用operation对象,你可以配置operation对象之间的依赖性按顺序执行任务,这和condition差不多)


lock 和condition在并发设计中还算常见,原子操作是另外一种保护和同步获取数据的方法。在执行表量数据的数学或者逻辑操作时,原子操作提供了一个代替锁的轻量级的方法。原子操作使用特别的硬件指令保证在更改某个变量完成后另外一个线程才可以反问。你可以访问“Synchronization Tools.”

For more information about the available synchronization tools, see “Synchronization Tools.”


欢迎进入iOS开发群146273732 讨论

About Threaded Programming


For many years, maximum computer performance was limited largely by the speed of a single microprocessor at the heart of the computer. As the speed of individual processors started reaching their practical limits, however, chip makers switched to multicore designs, giving the computer the opportunity to perform multiple tasks simultaneously. And although OS X takes advantage of these cores whenever it can to perform system-related tasks, your own applications can also take advantage of them through threads.


What Are Threads?

什么是线程呢?

Threads are a relatively lightweight way to implement multiple paths of execution inside of an application. At the system level, programs run side by side, with the system doling out execution time to each program based on its needs and the needs of other programs. Inside each program, however, exists one or more threads of execution, which can be used to perform different tasks simultaneously or in a nearly simultaneous manner. The system itself actually manages these threads of execution, scheduling them to run on the available cores and preemptively interrupting them as needed to allow other threads to run.

线程是应用内实现多个执行路径的轻量级方法。在系统级别,程序并排地运行,系统根据每个程序的需求分配执行时间。在每个应用内容,可能一个或者多个执行线程,这样可以同步地或者以类似同步的方式执行不同的任务。实际上,系统本身管理着这些执行线程,安排他们运行可用的内核,或者俺需要打断他们允许其他线程运行。

From a technical standpoint, a thread is a combination of the kernel-level and application-level data structures needed to manage the execution of code. The kernel-level structures coordinate the dispatching of events to the thread and the preemptive scheduling of the thread on one of the available cores. The application-level structures include the call stack for storing function calls and the structures the application needs to manage and manipulate the thread’s attributes and state.

从技术角度讲,一个线程结合了内核级以及应用级的数据结构来管理代码的执行。内核级的结构协调派发给线程的事件以及线程的优先级调度。应用级的结构包括存储函数调用的栈,以及管理和操作线程属性和状态的结构。

In a non-concurrent application, there is only one thread of execution. That thread starts and ends with your application’s main routine and branches one-by-one to different methods or functions to implement the application’s overall behavior. By contrast, an application that supports concurrency starts with one thread and adds more as needed to create additional execution paths. Each new path has its own custom start routine that runs independently of the code in the application’s main routine. Having multiple threads in an application provides two very important potential advantages:

  • Multiple threads can improve an application’s perceived responsiveness.

  • Multiple threads can improve an application’s real-time performance on multicore systems.

在非并发的应用中,只有一个执行线程。这个线程随着main 程序 开始和结束。并且一个接一个地到不同的方法或者函数中实现应用的所有行为。相比之下,支持并发的应用以一个线程开始,并且可以根据需要创建额外的执行路径。每一个新的路径都有它自己的开始程序在应用的主程序中运行代码。应用中的多线程提供了两个很重要的潜在优势

1,多线程可以提高应用的响应能力

2,多线程可以提高多核系统的实时性能

If your application has only one thread, that one thread must do everything. It must respond to events, update your application’s windows, and perform all of the computations needed to implement your application’s behavior. The problem with having just one thread is that it can only do one thing at a time. So what happens when one of your computations takes a long time to finish? While your code is busy computing the values it needs, your application stops responding to user events and updating its windows. If this behavior continues long enough, a user might think your application is hung and try to forcibly quit it. If you moved your custom computations onto a separate thread, however, your application’s main thread would be free to respond to user interactions in a more timely manner.

如果你的应用只有一个线程,那么这个线程就得做一切。它必须响应事件,更新应用得窗口,执行所有计算去实现应用功能。仅仅使用一个线程的问题是同一刻它只能做一件事。那么假如你的某个计算任务需要花很久才能完成呢? 你的应用忙于计算,而应用停止了用户响应,无法更新窗口。如果这样持续了很久,用户会认为你的应用不工作了,然后强制退出。如果你把这些计算移到一个单独的线程,这样就可以即时响应用户操作了。

With multicore computers common these days, threads provide a way to increase performance in some types of applications. Threads that perform different tasks can do so simultaneously on different processor cores, making it possible for an application to increase the amount of work it does in a given amount of time.

随着多核计算机的流行,线程可以增加某些应用的性能。线程可以在不同的处理器内核上同步执行不同的任务,这样应用可以提高速度。

Of course, threads are not a panacea for fixing an application’s performance problems. Along with the benefits offered by threads come the potential problems. Having multiple paths of execution in an application can add a considerable amount of complexity to your code. Each thread has to coordinate its actions with other threads to prevent it from corrupting the application’s state information. Because threads in a single application share the same memory space, they have access to all of the same data structures. If two threads try to manipulate the same data structure at the same time, one thread might overwrite another’s changes in a way that corrupts the resulting data structure. Even with proper protections in place, you still have to watch out for compiler optimizations that introduce subtle (and not so subtle) bugs into your code.

当然了,线程并不是解决应用性能问题的万能药。线程的优点也伴随着潜在的问题。 使用多线程会让代码比较复杂度。每个线程都必须和其他线程配合防止弄错应用的状态信息。因为在单一应用中,线程共享相同的内存空间,它们访问所有相同的数据结构。如果两个线程想要同时操作相同的数据,这样会导致数据错乱。尽管有着保护,但你还是要小心编译器优化代理的显式或者隐式的错误。

Threading Terminology

线程术语

Before getting too far into discussions about threads and their supporting technologies, it is necessary to define some basic terminology.

If you are familiar with UNIX systems, you may find that the term “task” is used differently by this document. On UNIX systems, the term “task” is used at times to refer to a running process.

This document adopts the following terminology:

  • The term thread is used to refer to a separate path of execution for code.

  • The term process is used to refer to a running executable, which can encompass multiple threads.

  • The term task is used to refer to the abstract concept of work that needs to be performed.


Alternatives to Threads

线程的替代技术 [暂时不翻译了]

One problem with creating threads yourself is that they add uncertainty to your code. Threads are a relatively low-level and complicated way to support concurrency in your application. If you do not fully understand the implications of your design choices, you could easily encounter synchronization or timing issues, the severity of which can range from subtle behavioral changes to the crashing of your application and the corruption of the user’s data.

Another factor to consider is whether you need threads or concurrency at all. Threads solve the specific problem of how to execute multiple code paths concurrently inside the same process. There may be cases, though, where the amount of work you are doing does not warrant concurrency. Threads introduce a tremendous amount of overhead to your process, both in terms of memory consumption and CPU time. You may discover that this overhead is too great for the intended task, or that other options are easier to implement.

Table 1-1 lists some of the alternatives to threads. This table includes both replacement technologies for threads (such as operation objects and GCD) and alternatives that are geared towards efficiently using the single thread you already have.

Table 1-1  Alternative technologies to threads

Technology

Description

Operation objects

Introduced in OS X v10.5, an operation object is a wrapper for a task that would normally be executed on a secondary thread. This wrapper hides the thread management aspects of performing the task, leaving you free to focus on the task itself. You typically use these objects in conjunction with an operation queue object, which actually manages the execution of the operation objects on one or more threads.

For more information on how to use operation objects, see Concurrency Programming Guide.

Grand Central Dispatch (GCD)

Introduced in Mac OS x v10.6, Grand Central Dispatch is another alternative to threads that lets you focus on the tasks you need to perform rather than on thread management. With GCD, you define the task you want to perform and add it to a work queue, which handles the scheduling of your task on an appropriate thread. Work queues take into account the number of available cores and the current load to execute your tasks more efficiently than you could do yourself using threads.

For information on how to use GCD and work queues, see Concurrency Programming Guide

Idle-time notifications

For tasks that are relatively short and very low priority, idle time notifications let you perform the task at a time when your application is not as busy. Cocoa provides support for idle-time notifications using the NSNotificationQueue object. To request an idle-time notification, post a notification to the default NSNotificationQueue object using theNSPostWhenIdle option. The queue delays the delivery of your notification object until the run loop becomes idle. For more information, see Notification Programming Topics.

Asynchronous functions

The system interfaces include many asynchronous functions that provide automatic concurrency for you. These APIs may use system daemons and processes or create custom threads to perform their task and return the results to you. (The actual implementation is irrelevant because it is separated from your code.) As you design your application, look for functions that offer asynchronous behavior and consider using them instead of using the equivalent synchronous function on a custom thread.

Timers

You can use timers on your application’s main thread to perform periodic tasks that are too trivial to require a thread, but which still require servicing at regular intervals. For information on timers, see “Timer Sources.”

Separate processes

Although more heavyweight than threads, creating a separate process might be useful in cases where the task is only tangentially related to your application. You might use a process if a task requires a significant amount of memory or must be executed using root privileges. For example, you might use a 64-bit server process to compute a large data set while your 32-bit application displays the results to the user.

Warning: When launching separate processes using the fork function, you must always follow a call to fork with a call to exec or a similar function. Applications that depend on the Core Foundation, Cocoa, or Core Data frameworks (either explicitly or implicitly) must make a subsequent call to an exec function or those frameworks may behave improperly.

Threading Support

If you have existing code that uses threads, OS X and iOS provide several technologies for creating threads in your applications. In addition, both systems also provide support for managing and synchronizing the work that needs to be done on those threads. The following sections describe some of the key technologies that you need to be aware of when working with threads in OS X and iOS.

Threading Packages

Although the underlying implementation mechanism for threads is Mach threads, you rarely (if ever) work with threads at the Mach level. Instead, you usually use the more convenient POSIX API or one of its derivatives. The Mach implementation does provide the basic features of all threads, however, including the preemptive execution model and the ability to schedule threads so they are independent of each other.

尽管线程核心实现机制是Mach 线程,但是你很少使用。 相反,你经常使用更方便的 POSIX API或者它的衍生技术。

Listing 2-2 lists the threading technologies you can use in your applications.

下面的列出了你可以使用的线程技术

Table 1-2  Thread technologies

Technology

Description

Cocoa threads

Cocoa implements threads using the NSThread class. Cocoa also provides methods on NSObject for spawning new threads and executing code on already-running threads. For more information, see “Using NSThread” and “Using NSObject to Spawn a Thread.”

POSIX threads

POSIX threads provide a C-based interface for creating threads. If you are not writing a Cocoa application, this is the best choice for creating threads. The POSIX interface is relatively simple to use and offers ample flexibility for configuring your threads. For more information, see “Using POSIX Threads”

Multiprocessing Services

Multiprocessing Services is a legacy C-based interface used by applications transitioning from older versions of Mac OS. This technology is available in OS X only and should be avoided for any new development. Instead, you should use theNSThread class or POSIX threads. If you need more information on this technology, see Multiprocessing Services Programming Guide.

1,Cocoa 使用NSThread类实现线程。Cocoa也提供NSObject的方法生产新的线程。可以参考 “Using NSThread” and “Using NSObject to Spawn a Thread.”

2,POSIX线程提供了基于C的接口来创建线程。如果你写的不是Cocoa程序,这是最好的选择。可以参考“Using POSIX Threads”

At the application level, all threads behave in essentially the same way as on other platforms. After starting a thread, the thread runs in one of three main states: running, ready, or blocked. If a thread is not currently running, it is either blocked and waiting for input or it is ready to run but not scheduled to do so yet. The thread continues moving back and forth among these states until it finally exits and moves to the terminated state.

在应用程序级别,所有的线程行为本质上是一样的。一个线程启动后,线程以三种主要状态(runing,ready 后者blocked)之一运行着。如果一个线程不在runing状态,那它要么是被阻塞等待输入要么是装备是运行但是还没开始。线程继续在这些状态中来回切换,直到最后退出,变成终止状态。

When you create a new thread, you must specify an entry-point function (or an entry-point method in the case of Cocoa threads) for that thread. This entry-point function constitutes the code you want to run on the thread. When the function returns, or when you terminate the thread explicitly, the thread stops permanently and is reclaimed by the system. Because threads are relatively expensive to create in terms of memory and time, it is therefore recommended that your entry point function do a significant amount of work or set up a run loop to allow for recurring work to be performed.

当你创建一个新的线程,你必须为线程指定一个入口点函数。这个函数构建你想要运行在这个线程的代码。当函数返回时,或者当你终结线程时,线程被永远的停止,并且被系统回收。因为考虑到内存和时间,创建线程代价相对不小,所以你的入口点函数要做一些重要的工作,或者建立一个run loop 运行执行。

For more information about the available threading technologies and how to use them, see “Thread Management.”

对于更多线程相关的技术,你可以参考“Thread Management.”

Run Loops


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.

Run loop 是管理异步到达线程事件的基础设施。run loop 监控线程的一个或者多个事件源。事件到达时,系统唤醒线程,派发事件给run loop,run loop然后派发事件给你指定的处理器。如果没有事件要处理,那么run loop会让线程睡眠。

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. 

并不强制为每个线程使用run loop,但是使用了可以提供更好的用户体验。Run loop可以用最少的资源创建生存期长的线程。由于没什么需要做时run loop会让线程睡眠,它消除了轮询。

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 OS X 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.

配置一个run loop ,你所要做的只是开启你的线程,引用run loop 对象,安装你的事件处理器,然后让run loop 运行。OS X为你自动配置了主线程的run loop。如果是你自己的线程,你要自己配置run loop.你可以参考“Run Loops.”

Details about run loops and examples of how to use them are provided in “Run Loops.”

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.

线程编程的危险之一就是多线程之间的资源争夺。如果多个线程想要同时使用或者更改资源,糟糕的问题就会出现。其中一种缓解问题的办法是排除共享资源。确保每个线程在不同的资源集合上操作。如果这不行,那么你必须使用lock ,条件,原子操作或者其他技术来同步访问资源。

Locks provide a brute force form of protection for code that can be executed by only one thread at a time. The most common type of lock is mutual exclusion lock, also known as a mutex. When a thread tries to acquire a mutex that is currently held by another thread, it blocks until the lock is released by the other thread. Several system frameworks provide support for mutex locks, although they are all based on the same underlying technology. In addition, Cocoa provides several variants of the mutex lock to support different types of behavior, such as recursion. For more information about the available types of locks, see “Locks.”

锁提供了保护代码的粗暴方式,那就是一次只能有一个线程执行。最常见的锁是可变排斥锁,或者叫mutex. 当一个线程想要获取被其他线程保持的锁,它就会阻塞直到锁被释放。一些系统框架提供对mutex锁的支持,尽管它们都是基于同样的底层技术。另外,Cocoa还提供mutex 锁一些变量以支持不同的行为,比如循环。可以参考 “Locks.”

In addition to locks, the system provides support for conditions, which ensure the proper sequencing of tasks within your application. A condition acts as a gatekeeper, blocking a given thread until the condition it represents becomes true. When that happens, the condition releases the thread and allows it to continue. The POSIX layer and Foundation framework both provide direct support for conditions. (If you use operation objects, you can configure dependencies among your operation objects to sequence the execution of tasks, which is very similar to the behavior offered by conditions.)

除了锁,系统还提供了条件,这会保证应用内的任务按顺序执行。一个condition就像一个门卫,阻塞一个给定的线程直到condition为真。为真时,condition就会释放线程,允许它继续执行。POSIX层和Foundation框架都对condition提供了直接的支持。(如果你使用operation对象,你可以配置operation对象之间的依赖性按顺序执行任务,这和condition差不多)

Although locks and conditions are very common in concurrent design, atomic operations are another way to protect and synchronize access to data. Atomic operations offer a lightweight alternative to locks in situations where you can perform mathematical or logical operations on scalar data types. Atomic operations use special hardware instructions to ensure that modifications to a variable are completed before other threads have a chance to access it.

lock 和condition在并发设计中还算常见,原子操作是另外一种保护和同步获取数据的方法。在执行表量数据的数学或者逻辑操作时,原子操作提供了一个代替锁的轻量级的方法。原子操作使用特别的硬件指令保证在更改某个变量完成后另外一个线程才可以反问。你可以访问“Synchronization Tools.”

For more information about the available synchronization tools, see “Synchronization Tools.”

Inter-thread Communication

线程间的通信

Although a good design minimizes the amount of required communication, at some point, communication between threads becomes necessary. (A thread’s job is to do work for your application, but if the results of that job are never used, what good is it?) Threads may need to process new job requests or report their progress to your application’s main thread. In these situations, you need a way to get information from one thread to another. Fortunately, the fact that threads share the same process space means you have lots of options for communication.

There are many ways to communicate between threads, each with its own advantages and disadvantages. “Configuring Thread-Local Storage”lists the most common communication mechanisms you can use in OS X. (With the exception of message queues and Cocoa distributed objects, these technologies are also available in iOS.) The techniques in this table are listed in order of increasing complexity.

线程间的通信方法很多。下面列出了OS X中常见的线程间通信机制。(除了message queues 和 Cocoa distributed objects ,其他iOS也能用)

Table 1-3  Communication mechanisms

Mechanism

Description

Direct messaging

Cocoa applications support the ability to perform selectors directly on other threads. This capability means that one thread can essentially execute a method on any other thread. Because they are executed in the context of the target thread, messages sent this way are automatically serialized on that thread. For information about input sources, see “Cocoa Perform Selector Sources.”

Global variables, shared memory, and objects

Another simple way to communicate information between two threads is to use a global variable, shared object, or shared block of memory. Although shared variables are fast and simple, they are also more fragile than direct messaging. Shared variables must be carefully protected with locks or other synchronization mechanisms to ensure the correctness of your code. Failure to do so could lead to race conditions, corrupted data, or crashes.

Conditions

Conditions are a synchronization tool that you can use to control when a thread executes a particular portion of code. You can think of conditions as gate keepers, letting a thread run only when the stated condition is met. For information on how to use conditions, see “Using Conditions.”

Run loop sources

A custom run loop source is one that you set up to receive application-specific messages on a thread. Because they are event driven, run loop sources put your thread to sleep automatically when there is nothing to do, which improves your thread’s efficiency. For information about run loops and run loop sources, see “Run Loops.”

Ports and sockets

Port-based communication is a more elaborate way to communication between two threads, but it is also a very reliable technique. More importantly, ports and sockets can be used to communicate with external entities, such as other processes and services. For efficiency, ports are implemented using run loop sources, so your thread sleeps when there is no data waiting on the port. For information about run loops and about port-based input sources, see “Run Loops.”

Message queues

The legacy Multiprocessing Services defines a first-in, first-out (FIFO) queue abstraction for managing incoming and outgoing data. Although message queues are simple and convenient, they are not as efficient as some other communications techniques. For more information about how to use message queues, see Multiprocessing Services Programming Guide.

Cocoa distributed objects

Distributed objects is a Cocoa technology that provides a high-level implementation of port-based communications. Although it is possible to use this technology for inter-thread communication, doing so is highly discouraged because of the amount of overhead it incurs. Distributed objects is much more suitable for communicating with other processes, where the overhead of going between processes is already high. For more information, see Distributed Objects Programming Topics.

Design Tips

The following sections offer guidelines to help you implement threads in a way that ensures the correctness of your code. Some of these guidelines also offer tips for achieving better performance with your own threaded code. As with any performance tips, you should always gather relevant performance statistics before, during, and after you make changes to your code.

Avoid Creating Threads Explicitly

Writing thread-creation code manually is tedious and potentially error-prone and you should avoid it whenever possible. OS X and iOS provide implicit support for concurrency through other APIs. Rather than create a thread yourself, consider using asynchronous APIs, GCD, or operation objects to do the work. These technologies do the thread-related work behind the scenes for you and are guaranteed to do it correctly. In addition, technologies such as GCD and operation objects are designed to manage threads much more efficiently than your own code ever could by adjusting the number of active threads based on the current system load. For more information about GCD and operation objects, seeConcurrency Programming Guide.

Keep Your Threads Reasonably Busy

If you decide to create and manage threads manually, remember that threads consume precious system resources. You should do your best to make sure that any tasks you assign to threads are reasonably long-lived and productive. At the same time, you should not be afraid to terminate threads that are spending most of their time idle. Threads use a nontrivial amount of memory, some of it wired, so releasing an idle thread not only helps reduce your application’s memory footprint, it also frees up more physical memory for other system processes to use.

Important: Before you start terminating idle threads, you should always record a set of baseline measurements of your applications current performance. After trying your changes, take additional measurements to verify that the changes are actually improving performance, rather than hurting it.

Avoid Shared Data Structures

The simplest and easiest way to avoid thread-related resource conflicts is to give each thread in your program its own copy of whatever data it needs. Parallel code works best when you minimize the communication and resource contention among your threads.

Creating a multithreaded application is hard. Even if you are very careful and lock shared data structures at all the right junctures in your code, your code may still be semantically unsafe. For example, your code could run into problems if it expected shared data structures to be modified in a specific order. Changing your code to a transaction-based model to compensate could subsequently negate the performance advantage of having multiple threads. Eliminating the resource contention in the first place often results in a simpler design with excellent performance.

Threads and Your User Interface

If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updates from your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks, such as Cocoa, generally require this behavior, but even for those that do not, keeping this behavior on the main thread has the advantage of simplifying the logic for managing your user interface.

There are a few notable exceptions where it is advantageous to perform graphical operations from other threads. For example, you can use secondary threads to create and process images and perform other image-related calculations. Using secondary threads for these operations can greatly increase performance. If you are not sure about a particular graphical operation though, plan on doing it from your main thread.

For more information about Cocoa thread safety, see “Thread Safety Summary.” For more information about drawing in Cocoa, see Cocoa Drawing Guide.

Be Aware of Thread Behaviors at Quit Time

A process runs until all non-detached threads have exited. By default, only the application’s main thread is created as non-detached, but you can create other threads that way as well. When the user quits an application, it is usually considered appropriate behavior to terminate all detached threads immediately, because the work done by detached threads is considered optional. If your application is using background threads to save data to disk or do other critical work, however, you may want to create those threads as non-detached to prevent the loss of data when the application exits.

Creating threads as non-detached (also known as joinable) requires extra work on your part. Because most high-level thread technologies do not create joinable threads by default, you may have to use the POSIX API to create your thread. In addition, you must add code to your application’s main thread to join with the non-detached threads when they do finally exit. For information on creating joinable threads, see“Setting the Detached State of a Thread.”

If you are writing a Cocoa application, you can also use the applicationShouldTerminate: delegate method to delay the termination of the application until a later time or cancel it altogether. When delaying termination, your application would need to wait until any critical threads have finished their tasks and then invoke the replyToApplicationShouldTerminate: method. For more information on these methods, seeNSApplication Class Reference.

Handle Exceptions

Exception handling mechanisms rely on the current call stack to perform any necessary clean up when an exception is thrown. Because each thread has its own call stack, each thread is therefore responsible for catching its own exceptions. Failing to catch an exception in a secondary thread is the same as failing to catch an exception in your main thread: the owning process is terminated. You cannot throw an uncaught exception to a different thread for processing.

If you need to notify another thread (such as the main thread) of an exceptional situation in the current thread, you should catch the exception and simply send a message to the other thread indicating what happened. Depending on your model and what you are trying to do, the thread that caught the exception can then continue processing (if that is possible), wait for instructions, or simply exit.

Note: In Cocoa, an NSException object is a self-contained object that can be passed from thread to thread once it has been caught.

In some cases, an exception handler may be created for you automatically. For example, the @synchronized directive in Objective-C contains an implicit exception handler.

Terminate Your Threads Cleanly

The best way for a thread to exit is naturally, by letting it reach the end of its main entry point routine. Although there are functions to terminate threads immediately, those functions should be used only as a last resort. Terminating a thread before it has reached its natural end point prevents the thread from cleaning up after itself. If the thread has allocated memory, opened a file, or acquired other types of resources, your code may be unable to reclaim those resources, resulting in memory leaks or other potential problems.

For more information on the proper way to exit a thread, see “Terminating a Thread.”

Thread Safety in Libraries

Although an application developer has control over whether an application executes with multiple threads, library developers do not. When developing libraries, you must assume that the calling application is multithreaded or could switch to being multithreaded at any time. As a result, you should always use locks for critical sections of code.

For library developers, it is unwise to create locks only when an application becomes multithreaded. If you need to lock your code at some point, create the lock object early in the use of your library, preferably in some sort of explicit call to initialize the library. Although you could also use a static library initialization function to create such locks, try to do so only when there is no other way. Execution of an initialization function adds to the time required to load your library and could adversely affect performance.

Note: Always remember to balance calls to lock and unlock a mutex lock within your library. You should also remember to lock library data structures rather than rely on the calling code to provide a thread-safe environment.

If you are developing a Cocoa library, you can register as an observer for the NSWillBecomeMultiThreadedNotification if you want to be notified when the application becomes multithreaded. You should not rely on receiving this notification, though, as it might be dispatched before your library code is ever called.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值