iPhone 操作队列NSOperationQueue
iPhone 本身也支持多线程开发,同样, NSThread 类提供对多线程开发的支持时也面临多线程的共享数据管理和死锁问题,于是 iPhone 也提供了类似于 Java 线程池的解决方案:任务队列 NSOperationQueue 类。
和 Java 语言的 Runnable 接口一样, iPhone 提供了 NSOperation 接口进行任务对象的封装,而通过将任务对象加入到 NSOperationQueue 队列, NSOperationQueue 队列会分配线程进行任务对象的执行,任务对象的执行通过 - (void)main 方法,下面是典型的任务对象和任务队列的实现:
@interface ThreadPoolTask:NSOperation
{
}
@end
@implementation ThreadPoolTask
- (void)main
{
NSLog(@”start execute”);
}
@end
和 Java 语言中一样,构造一个多线程池并添加任务对象到线程池中,线程池会调用任务对象的 - (void)main 方法执行任务, iPhone 中典型的任务队列的实现如下:
NSOperationQueue* threadPool = [[NSOperation alloc] init];
[threadPool setMaxConcurrentOperationCount:4];
for(int i = 1;i <= 5;i++)
{
NSString* task = [NSString stringWithFormat:@”task %d”,i];
NSLog(@“put %@”,task);
[threadPool add:([[ThreadPoolTask alloc] init])];
}
可以看到, iPhone 通过 NSOperationQueue 提供了一套类似于线程池的机制,通过它可以更加方便的进行多线程的并发操作,从而使得程序员从繁杂的多线程共享数据管理和死锁问题中解脱出来。
iOS多线程编程概要总结
2. 创建线程的多种方法:
NSThread, POSIX(基于C语言支持的API), NSObject的performSelector
3. 在工程没有使用ARC时,线程执行体代码中创建自动释放池
4. 为了线程安全,尽量避免在线程中使用共享的数据结构
5. runloop: 线程中的一个事件处理循环,用来不停的调度工作以及处理输入事件.
每一个线程都有自己的runloop, 主线程是默认开启的,子线程是需要手动开启。
runloop监视每个输入源,处理事件,如果没有事件,runloop休眠,不消耗CPU资源
需要使用runloop的四种情况:
a.使用端口或自定义输入源和其他线程通信
b.子线程中使用了定时器
c.cocoa中使用任何performSelector到了线程中运行方法
d.使线程履行周期性任务
在子线程中用了NSURLConnection异步请求,那也需要用到runloop,不然线程退出了,相应的delegate方法就不能触发。
6. 线程同步: a. 原子操作
b. 内存屏障和volatile变量
内存屏障: 确保内存操作按照正确顺序工作的非阻塞同步工具
volatile : 一般编译器会优化代码通过加载这些变量的值进入寄存器。 对于线程之间共享的变量,使用volatile强制编译器每次都从内存读取数据,确保数据的同步。
c. 锁: mutex, @synchronized()指令
d. 信号量
7. 线程安全设计技巧
a. 完全避免数据同步(可能性比较小)
b. 了解同步的限制
c. 注意对代码正确性的威胁(保证数据同步安全,以及防止线程死锁)
d. 尽量只使用一个锁进行数据同步保护,避免产生死锁
e. 使用线程同步工具,
原子操作
锁: 使用POSIX, NSLock(lock,unlock, tryLock), @synchronized()指令,包含隐式的异常处理例程来保护代码,发生异常会释放资源。
8. 线程安全总结:
a. 不可改变的对象,通常是线程安全的
b. 主线程负责处理响应事件
线程安全的类和函数: NSArray, NSData, NSNumber.....
非线程安全: NSBundle, NSCoder, NSArchiver, NSMutableArray
只能用于主线程: NSAppleSript