--参考:(官网) https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html + (中文翻译) http://www.cocoachina.com/bbs/read.php?tid=87592
==关于多线程编程
------什么是多线程------
多线程是一个比较轻量级的方法在单个应用程序内实现同步完成多项任务。在系统级别内,程序并排执行,系统分配到每个程序的执行时间是基于该程序的所需时间 和其他程序的所需时间来决定的。然而在每个应程序的内部,存在一个或多个执行线 程,它同时或在一个几乎同时发生的方式里执行不同的任务。系统本身管理这些执行 的线程,调度它们在可用的内核上运行,并在需要让其他线程执行的时候抢先打断它们。
从技术角度来看,一个线程就是一个需要管理执行代码的内核级和应用级数据结 构组合。内核级结构协助调度线程事件,并抢占式调度一个线程到可用的内核之上。 应用级结构包括用于存储函数调用的调用堆栈和应用程序需要管理和操作线程属性 和状态的结构。
在非并发的应用程序,只有一个执行线程。该线程开始和结束于你应用程序的 main 循环,一个个方法和函数的分支构成了你整个应用程序的所有行为。与此相反, 支持并发的应用程序开始可以在需要额外的执行路径时候创建一个或多个线程。每个 新的执行路径有它自己独立于应用程序 main 循环的定制开始循环。在应用程序中存 在多个线程提供了两个非常重要的的潜在优势: 多个线程可以提高应用程序的感知响应。 多个线程可以提高应用程序在多核系统上的实时性能。
如果你的应用程序只有单独的线程,那么该独立程序需要完成所有的事情。它必须对事件作出响应,更新您的应用程序的窗口,并执行所有实现你应用程序行为需要 的计算。拥有单独线程的主要问题是在同一时间里面它只能执行一个任务。那么当你 的应用程序需要很长时间才能完成的时候会发生什么呢?当你的代码忙于计算你所需要的值的时候,你的程序就会停止响应用户事件和更新它的窗口。如果这样的情况 持续足够长的时间,用户就会误认为你的程序被挂起了,并试图强制退出。如果你把 你的计算任务转移到一个独立的线程里面,那么你的应用程序主线程就可以自由并及 时响应用户的交互。
当然多线程并不是解决程序性能问题的灵丹妙药。多线程带来好处同时也伴随着 潜在问题。应用程序内拥有多个可执行路径,会给你的代码增加更多的复杂性。每个线程需要和其他线程协调其行为,以防止它破坏应用程序的状态信息。因为应用程序内的多个线程共享内存空间,它们访问相同的数据结构。如果两个线程试图同时处理相同的数据结构,一个线程有可能覆盖另外线程的改动导致破坏该数据结构。即使有适当的保护,你仍然要注意由于编译器的优化导致给你代码产生很微妙的(和不那么微妙)的Bug。
------线程术语(Terminology)------
--线程(thread)用于指代独立执行的代码段。
--进程(process)用于指代一个正在运行的可执行程序,它可以包含多个线程。
--任务(task)用于指代抽象的概念,表示需要执行工作
------多线程的替代方法:------
你自己创建多线程代码的一个问题就是它会给你的代码带来不确定性。多线程是一个相对底层的水平和复杂的方式来支持你的应用程序并发。如果你不完全理解你的 设计选择的影响,你可能很容易遇到同步或定时问题,其范围可以从细微的行为变化到严重到让你的应用程序崩溃并破坏用户数据。
你需要考虑的另一个因素是你是否真的需要多线程或并发。多线程解决了如何在同一个进程内并发的执行多路代码路径的问题。然而在很多情况下你是无法保证你所在做的工作是并发的。多线程引入带来大量的开销,包括内存消耗和 CPU 占用。你会发现这些开销对于你的工作而言实在太大,或者有其他方法会更容易实现。下面列举了多线程的替代方法:
技术 | 描述 |
Operation objects | Introduced in Mac 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 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 the NSPostWhenIdle option. The queue delays the delivery of your notification object until the run loop becomes idle. For more information, see Notification Programming Top |
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. |
-------线程支持:-------
如果你已经有代码使用了多线程,Mac OS X 和 iOS 提供几种技术来在你的应用程 序里面创建多线程。此外,两个系统都提供了管理和同步你需要在这些线程里面处理 的工作。以下几个部分描述了一些你在 Mac OS X 和 iOS 上面使用多线程的时候需要注意的关键技术:
--线程包(Threading Packages)
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”小节和“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 the NSThread class or POSIX threads. If you need more information on this technology, see Multiprocessing Services Programming Guide. |
--Run Loops
你创建线程的时候不需要使用一个 run loop,但是如果你这么做的话可以给用户 带来更好的体验。Run Loops 可以让你使用最小的资源来创建长时间运行线程。因为 run loop 在没有任何事件处理的时候会把它的线程置于休眠状态,它消除了消耗 CPU 周期轮询,并防止处理器本身进入休眠状态并节省电源。
为了配置 run loop,你所需要做的是启动你的线程,获取 run loop 的对象引用, 设置你的事件处理程序,并告诉 run loop 运行。Cocoa 和 Carbon 提供的基础设施会 自动为你的主线程配置相应的 run loop。如果你打算创建长时间运行的辅助线程, 那么你必须为你的线程配置相应的 run loop。详见“Run Loops”小节。
--同步工具(Synchronization Tools)
锁提供了一次只有一个线程可以执行代码的有效保护形式。最普遍的一种锁是互斥排他锁,也就是我们通常所说的“mutex”。当一个线程试图获取一个当前已经被其他线程占据的互斥锁的时候,它就会被阻塞直到其他线程释放该互斥锁。系统的几个框架提供了对互斥锁的支持,虽然它们都是基于相同的底层技术。此外 Cocoa 提供了 几个互斥锁的变种来支持不同的行为类型,比如递归。详见“Locks”小节。
除了锁,系统还提供了条件(conditions),确保在你的应用程序任务执行的适当顺序。一个条 件作为一个看门人,阻塞给定的线程,直到它代表的条件变为真。当发生这种情况的时候,条件释放该线程并允许它继续执行。POSIX 级别和基础框架都直接提供了条件 的支持。(如果你使用操作对象,你可以配置你的操作对象之间的依赖关系的顺序确 定任务的执行顺序,这和条件提供的行为非常相似)。
尽管锁和条件在并发设计中使用非常普遍,原子操作也是另外一种保护和同步访 问数据的方法。原子操作在以下情况的时候提供了替代锁的轻量级的方法,其中你可 以执行标量数据类型的数学或逻辑运算。原子操作使用特殊的硬件设施来保证变量的 改变在其他线程可以访问之前完成。详见“同步工具”小节
--线程间通信
线程间通信有很多种方法,每种都有它的优点和缺点。“配置线程局部存储”列 出了很多你可以在 Mac OS X 上面使用的通信机制。(异常的消息队列和 Cocoa 分布式 对象,这些技术也可在 iOS 用来通信)。本表中的技术是按照复杂性的顺序列出:
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 “Run Loops”章节 |
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 “同步工具”小节 |
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. |
------设计技巧-------
--避免显式创建线程(Creating Threads Explicitly)
--保持你的线程合理的忙
--避免共享数据结构
创建多线程的应用是很困难的。即使你非常小心,并且在你的代码里面所有正确的地方锁住共享资源,你的代码依然可能语义不安全的。比如,当需要在一个特定的顺序里面修改共享数据结构的时候,你的代码有可能遇到问题。以基于事务的方式修改你的代码, 来弥补可能随后对多线程性能产生损耗的情况。把避免资源争夺放在首位通常可以得 到简单的设计同样具有高性能的效果。
--多线程和你的用户界面
有几个显著的例外,在其他线程执行图形操作非常有利。你可以使用其它线程来创建和处理图片和其他图片相关的计算。使用其它线程来执行这些操作可以极大提高性能。如果你不确定一个图像处理操作是否应该放在其它线程,那么你应该在主线程执行这些操作。关于线程安全可以详见 “线程安全总结”章节。
--了解Quit Time时线程的行为(Thread Behaviors)
进程(process)一直运行直到所有non-detached线程都已经退出为止。默认情况下,只有应用程序的主线程是non-detached的,但是你也可以使用同样的方法来创建其他线程。 当用户退出程序的时候,通常考虑适当的立即中断所有detached线程,因为通常detached线程所做的工作都是是可选(optional)的。如果你的应用程序要使用后台线程来保存数据到硬盘或者做其他周期行的工作,那么你可能需要把这些线程创建为non-detached的,来保证程序退出的时候不丢失数据。
如果你正在编程 Cocoa 的程序,你也可以通过使用 applicationShouldTerminate: 的委托方法来延迟程序的中断直到一段时间后或者完全取消。当延迟中断的时候,你的程序需要等待直到任何周期线程已经完成它们的任务且调用了 replyToApplicationShouldTerminate:方法。详细请参考 NSApplication Class Reference
--处理异常
如果你需要通知另一个线程(比如主线程)处理当前线程中的一个异常情况,你应该捕捉异常,并简单地将消息发送到其他线程告知发生了什么事。根据你的模型和你正在尝试做的事情,引发异常的线程可以继续执行(如果可能的话)、等待指示、或者干脆退出。
注意:在 Cocoa 里面,一个NSException对象是 一个自包含(self-contained)对象,一旦它被捕捉到,那么它可以从一个线程传递到另外一个线程。
在一些情况下,异常处理可能是自动创建的。比如,Objective-C 中的 @synchronized 包含了一个隐式的异常处理。
--干净地中断你的线程
--线程安全的库(Thread Safety in Libraries)
对类库开发者而言,只当应用程序是多线程的时候才创建锁是不明智的。如果你需要锁定你代码中的某些部分,早期应该创建锁对象给你的类库使用,更好是显式调用初始化类库。虽然你也可以使用静态库的初始化函数来创建这些锁,但是仅当没有其他方式的才应该这样做。执行初始化函数需要延长加载你类库的时间,且可能对你程序性能造成不利影响。
注意:永远记住在你的类库里面保持锁和释放锁的操作平衡。你应该总是记住锁定类库的数据结构,而不是依赖调用的代码提供线程安全环境。
如果你真正开发Cocoa的类库,那么当你想在应用程序变成多线程的时候收到通知的话,你可以给NSWillBecomeMultiThreadedNotification 注册一个观察者。不过你不应用依赖于这些收到的通知,因为它们可能在你的类库被调用之前已经被发出了。
==线程管理
当应用程序生成一个新的线程的时候,该线程变成应用程序进程空间内的一个独立的实体。每个线程都拥有它自己的执行栈( execution stack),由内核( kernel)调度独立的运行时间片。 一个线程可以和其他线程或其他进程通信,执行I/O操作,甚至执行任何你想要它完成的任务。因为它们处于相同的进程空间,所以一个独立应用程序里面的所有线程共享相同的虚拟内存空间(virtual memory space),并且具有和进程相同的访问权限。
本章提供了Mac OS X和iOS上面可用线程技术的预览,并给出了如何在你的应用程序里面使用它们的例子。
------线程成本(Thread Costs)------
表2-1量化了在你应用程序创建一个新的用户级线程所需的大致成本。这些成本里面的部分是可配置的,比如为辅助线程(secondary threads)分配堆栈空间的大小。创建一个线程所需的时间成本是粗略估计的,仅用于当互相比较的时候。线程创建时间很大程度依赖于处理器的负载,计算速度,和可用的系统和程序空间。
Kernel data structures | Approximately 1 KB | This memory is used to store the thread data structures and attributes, much of which is allocated as wired memory and therefore cannot be paged to disk. |
Stack space | 512 KB (secondary threads) 8 MB (OS X main thread) 1 MB (iOS main thread) | The minimum allowed stack size for secondary threads is 16 KB and the stack size must be a multiple of 4 KB. The space for this memory is set aside in your process space at thread creation time, but the actual pages associated with that memory are not created until they are needed. |
Creation time | Approximately 90 microseconds | This value reflects the time between the initial call to create the thread and the time at which the thread’s entry point routine began executing. The figures were determined by analyzing the mean and median values generated during thread creation on an Intel-based iMac with a 2 GHz Core Duo processor and 1 GB of RAM running OS X v10.5 |
------创建一个线程-------
--使用NSThread
--使用 detachNewThreadSelector:toTarget:withObject: 类方法来生成一个新的线程。
--创建 一个新的NSThread对象,并调用它的start方法。(仅在iOS和Mac OS X v10.5及其之后才支持)
这两种创建线程的技术都在你的应用程序里面新建了一个脱离的线程( detached thread)。 一个脱离的线程意味着当线程退出的时候线程的资源由系统自动回收。这也同样意味着之后不需要在其他线程里面显式的连接( join explicitly)。
[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];
在Mac OS X v10.5之前,你使用NSThread类来生成(spawn)多线程。虽然你可以获取一个NSThread对象并访问线程的属性,但你只能在线程运行之后在其内部做到这些。在Mac OS X v10.5支持
创建一个NSThread对象,而无需立即生成一个相应的新线程(这些在iOS里面同样可用)。新版支持使得在线程启动之前获取并设置线程的很多属性成为可能。
这也让用线程对象来引用正在运行的线程成为可能。
在Mac OS X v10.5及其之后初始化一个NSThread对象的简单方法是使用initWithTarget:selector:object:方法。该方法和detachNewThreadSelector:toTarget:withObject:方法参数信息基本一样,但创建了一个对象实例。然而它并没有启动一个线程。为了启动一个线程,你可以显式调用先对象的start方法,如下面代码
NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil];
[myThread start]; // Actually create the thread
备注:使用initWithTarget:selector:object:方法的替代办法是子类化NSThread,并重写它的main方法。你可以使用你重写的该方法的版本来实现你线程的主体入口。更多信息,请参阅
NSThread Class Reference 里面subclassing notes。
注意:虽然在线程间的偶尔通信的时候使用该方法很好,但是你不能周期的或频繁的使用performSelector:onThread:withObject:waitUntilDone:来实现线程间的通信。关于线程间通信的可选方法,详见“ Setting the Detached State of a Thread”小节
--使用POSIX的多线程
#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.
}
}
如果你把上面列表的代码添加到你任何一个源文件,并且调用LaunchThread函数,它将会在你的应用程序里面创建一个新的脱离线程。当然,新创建的线程使用该代码没有做任何有用的事情。线程将会加载并立即退出。为了让它更有兴趣,你需要添加代码到PosixThreadMainRoutine函数里面来做一些实际的工作。为了保证线程知道该干什么,你可以在创建的时候给线程传递一个数据的指针。把该指针作为pthread_create的最后一个参数。
为了在新建的线程里面和你应用程序的主线程通信,你需要建立一条和目标线程之间的稳定的通信路径。对于基于C语言的应用程序,有几种办法来实现线程间的通信,包括使用 端口(ports),条件(conditions)和共享内存(shared memory)。对于长期存在的线程,你应该几乎总是成立某种线程间的通信机制,让你的应用程序的主线程有办法来检查线程的状态或在应用程序退出时干净关闭它。关于更多介绍POSIX线程函数的信息,参考pthread的主页。
--使用NSObject来生成一个线程(Spawn a Thread)
// NSThread.h
@interface NSObject (NSThreadPerformAdditions)
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
// equivalent to the first method with kCFRunLoopCommonModes
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg NS_AVAILABLE(10_5, 2_0);
<span style="font-family: Arial, Helvetica, sans-serif;">[myObj performSelectorInBackground:@selector(doSomething) withObject:nil];</span>
调用该方法的效果和你在当前对象里面使用NSThread的detachNewThreadSelector:toTarget:withObject:传递selectore,object作为参数的方法一样。新的线程将会被立即生成并运行,它使用默认的设置。在selectore内部,你必须配置线程就像你在任何线程里面一样。比如,你可能需要设置一个自动释放池(如果你没有使用垃圾回收机制),配置线程的run loop如果你需要使用它。关于如果配置线程的信息,详见“
配置线程属性”小节
--在Cocoa程序上面使用POSIX线程
尽管NSThread类是Cocoa应用程序里面创建多线程的主要接口,如果方便的话你可以任意使用POSIX线程带替代。例如,如果你的代码里面已经使用了POSIX线程,而你又不想改写它的话,这时你可能需要使用POSIX多线程。如果你真打算在Cocoa程序里面使用POSIX线程,你应该了解如果在Cocoa和线程间交互,并遵循以下部分的一些指南:
1.保护Cocoa框架:
对于多线程的应用程序,Cocoa框架使用锁和其他内部同步(internal synchronization)方式来保证代码的正确执行。为了防止这些锁造成在单线程里面性能的损失,Cocoa直到应用程序使用NSThread类生成它的第一个新的线程的时候才创建这些锁。如果你仅且使用POSIX例程来生成新的线程,Cocoa不会收到关于你的应用程序当前变为多线程的通知。当这些刚好发生的时候,涉及Cocoa框架的操作可能会破坏甚至让你的应用程序崩溃。
如果你不确定Cocoa是否已经知道你的程序是多线程的,你可以使用NSThread的isMultiThreaded方法来检验一下。
2.混合POSIX和Cocoa的锁:
在同一个应用程序里面混合使用POSIX和Cocoa的锁很安全。Cocoa锁和条件对象(condition objects)基本上封装了POSIX的互斥体和条件。对于给定一个锁,你必须总是使用同样的接口来创建和操纵该锁。换言之,你不能使用Cocoa的NSLock对象来操纵一个你使用pthread_mutex_init函数生成的互斥体(mutex),反之亦然。
------配置线程属性(Configuring Thread Attributes)------
--配置线程的栈(Stack Size)大小
如果你想要改变一个给定线程的堆栈大小,你必须在创建该线程之前做一些操作。所有的线程技术提供了一些办法来设置线程堆栈的大小。虽然可以使用NSThread来设置栈大小,但是它只能在iOS和Mac OS X v10.5及其之后才可用。下面列出了每种技术的对于栈大小的设置:
Cocoa | In iOS and OS X v10.5 and later, allocate and initialize an NSThread object (do not use the detachNewThreadSelector: toTarget:withObject: method). Before calling the start method of the thread object, use the setStackSize: 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. |
--配置线程本地(Thread-Local)存储
Cocoa和POSIX以不同的方式保存线程的字典,所以你不能混淆并同时调用者两种技术。然而只要你在你的线程代码里面坚持使用了其中一种技术,最终的结果应该是一样的。在Cocoa里面,你使用NSThread的 threadDictionary方法来检索一个NSMutableDictionary对象,你可以在它里面添加任何线程需要的键。在POSIX里面,你使用 pthread_setspecific和 pthread_getspecific函数来设置和访问你线程的键和值。
--设置线程的脱离(Detached State)状态
你可以认为可连接( Joinable )线程类似于子线程。虽然它作为独立线程运行, 但是可连接线程在它资源可以被系统回收之前必须被其他线程连接。可连接线程同时提供了一个显示的方式来把数据从一个正在退出的线程传递到其他线程。在它退出之前,可连接线程可以传递一个数据指针或者其他返回值给pthread_exit函数。其他线程可以通过pthread_join函数来拿到这些数据。
--设置线程的优先级(Priority)
<pre name="code" class="objc">//NSThread.h
- (double)threadPriority NS_AVAILABLE(10_6, 4_0); // 优先级- (void)setThreadPriority:(double)p NS_AVAILABLE(10_6, 4_0);
- (NSMutableDictionary *)threadDictionary; //本地存储
- (NSUInteger)stackSize NS_AVAILABLE(10_5, 2_0); // 栈大小- (void)setStackSize:(NSUInteger)s NS_AVAILABLE(10_5, 2_0);
------编写你线程的主体入口点(Entry Routine)------
--创建一个自动释放池
如果应用程序使用的垃圾回收机制( garbage collection),而不是管理的内存模型( managed memory model,),那么创建一个自动释放池不是绝对必要的。在垃圾回收的应用程序里面,一个自动释放池是无害的,而且大部分情况是被忽略。在代码模块(code module)必须同时支持垃圾回收和内存管理模型时自动释放池是必须的。在这种情况下,内存管理模型必须支持自动释放池,当应用程序运行垃圾回收的时候,自动释放池只是被忽略而已。
如果你的应用程序使 用内存管理模型,在你编写线程主体入口的时候第一件事情就是创建一个自动释放池。同样,在你的线程最后应该销毁该自动释放池。该池保证捕获自动释放对象。它不被release直到线程退出。以下显示了线程主体入口使用自动释放池的基本结构:
- (void)myThreadMainRoutine
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool
// Do thread work here.
[pool release]; // Release the objects in the pool.
}
因为顶层的自动释放池不会被释放直到线程退出。长时运行的线程应该
新建额外的自动释放池来更频繁的释放它的对象。比如,一个使用run loop的线程可能在每次运行完一次循环的时候创建并释放该自动释放池。更频繁的释放对象可以防止你的应用程序内存占用(
memory footprint)太大造成性能问题。对于任何与性能相关的行为,你应该测量你代码的实际表现,并适当地调整使用自动释放池。关于更多内存管理的信息和自动释放池,参阅“
Advanced Memory Management Programming Guide”
--设置异常处理(Exception Handler)
当在 Xcode 构建你项目的时候,你可以使用 C++或者 Objective-C 的异常处理风 格。 关于更多设置如何在 Objective-C里面抛出和捕获异常的信息,参阅“ Exception Programming Topics”
--设置一个 Run Loop
------(Terminating)中断线程------
如果你的应用程序需要在一个操作中间中断一个线程,你应该设计你的线程响应取消或退出的消息。 对于长时运行的操作(long-running operations),这意味着周期性停止工作来检查该消息是否到来。如果该消息的确到来并要求线程退出,那么线程就有机会来执行任何清理和退出工作;否则,它返回继续工作和处理下一个数据块。
响应取消消息的一个方法是使用run loop的输入源来接收这些消息。下面显示了类似代码在你的线程的主体入口里面是怎么样的(该示例显示了主循环部分,不包括设立一个自动释放池或配置实际的工作步骤)。该示例在 run loop 上面安装了一个自定义的输入源,它可以从其他线程接收消息。关于更多设置输入源的信息,参考“ 配置 Run Loop Sources”小节。执行一部分工作后,运行run loop的线程来查看是否有消息抵达输入源。如果没有,run loop 立即退出,并且循环继续处理 下一个数据块。因为该处理器并没有直接的访问 exitNow 局部变量,退出条件是通过 线程的字典来传输的。
- (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];
}
}
------------------------------------------------------------------我是分割线-------------------------------------------------------------------------