线程管理
线程管理包括创建,配置,退出三部分。主要包括创建线程的成本,线程创建,线程属性配置,线程主体入口函数编写,线程中断等
一,线程创建成本
1,为辅助线程分配的堆栈空间大小,便于系统和进程管理,以及为函数参数和局部变量分配空间
A,内核数据结构(kernel data structures)---大约1KB,This memory is used to store the thread data structures and attributes, much of which is allocated as wired memory and therefore cannot be
B,堆栈空间(Stack Space)---512 KB(secondary threads)/8 MB (Mac 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 need
2,创建线程需要的时间成本---大约90ms,创建时间依赖于处理器的负载,计算速度,和可用的系统和程序空间i
二,创建线程
创建线程需要做三部分工作:主体入口点,可用线程(用以启动被创建的线程),线程的属性配置。创建线程有很多方法,比如NSThread,POSIX,NSObject,及其他方法(Multiprocessing Services)等
1,使用NSThread创建线程
A,[NSThread detachNewThreadSelector:@selector(myThreadMainMethod:) toTarget:self withObject:nil];
线程的主体入口点是myThreadMainMethod,可用线程是self存在的线程,不过这个线程的属性不能在线程创建前配置,只能在线程内配置
B,创建一个新的NSThread实例,然后调用他的start方法启动线程
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod) withObject:nil];
[myThread start]; // Actually create the thread
线程主体入口是myThreadMainMethod,可用线程是self存在的线程,线程属性可以在创建一个新的NSThread实例之后,start方法调用之前设置
注:通过NSThread创建的线程,主要通过performSelector:onThread:withObject:withUntilDone方法进行线程间通信比较便捷的方法之一。
2,使用POSIX创建线程
A,Creating a thread in C
#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.
}
}
在上面的代码里,线程主体入口点是PosixThreadMainRoutine,通过pthread_create创建线程,在创建线程前初始化及配置线程属性pthread_attr_init/pthread_attr_setdetachstate
注:基于C语言的应用程序,常用的线程间通讯包括使用端口(ports),条件(conditions)和共享内存(shared memory)
3,使用NSObject创建线程
[myObj performSelectorInBackground:@selector(doSomething) withObject:nil];参照NSThread方法创建线程
4,使用其他技术创建线程
唯一一个可以考虑使用的是多处理 服务(Multiprocessing Services),它本身就是在 POSIX 线程上执行
5,在Cocoa程序里使用POSIX线程,需要注意的问题
A,Cocoa框架的保护
对于多线程应用程序,Cocoa框架使用锁和其他同步方式保证代码的正确使用。但是为了避免因为这些锁造成的单线程下的性能损失,Cocoa框架直到应用程序创建NSThread类生成它的第一个新线程的时候才创建这些锁。如果仅用POSIX创建新的线程,那么Cocoa框架将不会自动创建这些保护同步的锁。这样有可能因为这些造成Cocoa崩溃。解决办法:在创建第一个新线程之前,使用NSThread类生成一个线程,并让他立刻退出。这样保证Cocoa框架所需要的锁到位。
B,混合POSIX和Cocoa的锁
在同一应用程序里面可以混合使用POSIX和Cocoa的锁,因为Cocoa的锁和条件对象基本上只是封装POSIX的互斥体和条件。但是两者不能交叉使用,Cocoa的NSLock调用POSIX创建的mutex对象,反之依然
三,配置线程属性
线程属性主要包括线程的堆栈大小,本地存储,线程的脱离状态(detached),线程的优先级
1,配置线程的堆栈大小
A,Cocoa线程,只有通过New NSThread类的方法创建的线程才能配置线程的堆栈大小
[myThread setStackSize];
B,POSIX线程,创建一个限的pthread_attr_t数据结构,使用pthread_attr_setstacksize函数设置线程堆栈大小
2,配置线程本地存储---每个线程都维护一个键-值的字典结构,如何设置和访问这个结构呢?
A,Cocoa线程,NSMutableDictionary *mDict = [[NSThread currentThread] threadDictionary];
B,POSIX线程,通过pthread_setspecific设置这个结构,通过pthread_getspicific访问这个结构
3,设置线程的脱离状态
脱离线程(Detach Thread)---线程完成后,系统自动释放它所占用的内存空间
可连接线程(Joinable Thread)---一线程完成后,不回收可连接线程的资源
在应用程序退出时,脱离线程可以立即被中断,而可连接线程则不可以。每个可连接 线程必须在进程被允许可以退出的时候被连接。所以当线程处于周期性工作而不允许被中断的时 候,比如保存数据到硬盘,可连接线程是最佳选择。
如果你想要创建可连接线程,唯一的办法是使用 POSIX 线程。POSIX 默认创建的 线程是可连接的。通过pthread_attr_setdetachstate函数设置是否脱离属性
4,线程的优先级
A,Cocoa线程,[NSThread setThreadPriority];设置线程的优先级
B,POSIX线程,pthread_setschedparam函数实现优先级设置
注:高低线程交互时,要注意因为低线程的饥饿状态造成的阻塞,造成性能瓶颈,影响应用程序性能。
四,线程主体入口函数
在线程主体入口函数里面,主要做三个工作:
1,创建一个自动释放池(Autorelease pool),注意:要经常主动清理自动释放池里面的内存,提高线程的内存空间
2,设置异常处理
添加try/catch模块,捕获任何未知的异常,并提供适当的响应
3,设置一个run loop
对于需要动态处理到来的任务请求的线程,需要给线程添加一个run loop
五,中断线程
中断线程要注意的是,尽力保证线程从主体入口函数里面退出,这样能够保证线程的资源被自动释放。
- (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 method
[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 runUntilDoneDate:[NSDate date]];
// check the see if an input source handle changed the exitNow value.
exitNow = [[threadDict valueForKey:@"ThreadShouldExitNow"] boolValue];
}
}