iOS多线程编程
1. 进程,线程, 任务
进程:一个程序在运行时,系统会为其分配一个进程,用以管理他的一些资源。
线程:进程内所包含的一个或多个执行单元称为线程,线程一般情况下不持有资源,但可以使用其所在进程的资源。
任务:进程或线程中要做的事情。
在引入线程的操作系统中,通常把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。
线程比进程更小,对其调度的开销小,能够提高系统内多个任务的并发执行程度。
一个程序至少有一个进程,一个进程至少有一个线程.一个程序就是一个进程,而一个程序中的多个任务则被称为线程。
线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。应用程序(application)是由一个或多个相互协作的进程组成的。
多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源。
2. iOS多线程编程技术
共有三种(实际上是四种,还有Pthreads,C语言框架)
> NSThread
> NSOperation
> GCD (Grand Central Dispatch)
从上到下,封装度从低到高,封装度越高使用越简单,也是官方推荐使用。
三种方式的比较:
NSThread: 相对于另外两种是比较轻量级的。此种方式是经官方封装后的,是完全面向对象的,所以可以直接操控线程对象,非常直观方便。但是需要自己管理线程的生命周期,线程同步,而且线程同步对数据的加锁会有一定的系统开销。使用比较简单,但不够智能,在一些简单的场景才使用。
NSOperation: 完全面向对象的。相关的主要类有 NSOperation(任务) 和 NSOperationQueue(任务队列),其中NSOperationQueue 在iOS4引入GCD后,其内部用GCD实现的。此种方式下不需要关心线程管理,数据同步的操作。隐藏了许多复杂的代码,提供了简单的API。
GCD : 是苹果为多核并行运算提出的解决方案,会自动合理的利用更多的CPU内核(如双核,四核)。此种方式也能自动管理线程的生命周期(创建线程,调度任务,销毁线程),不需要自己管理,只需要告诉其要做的操作是什么就可以。虽然也使用c语言,但因为使用了block, 使用也很方便,灵活。在GCD中也有“任务”,“队列”的概念。
一、NSThread ===========================================
1. 使用NSThread创建并管理线程
> 创建线程
方式一:实例方法
[NSThread alloc] initWithTarget: selector : object:]
参数一:线程操作消息的接收者 self
参数二:选择器方法,线程操作的方法,此方法只能带一个参数,而且不能有返回值
参数三:线程操作方法所带的参数
使用该方式创建的线程不会自动启动,需调用线程方法:
[thread start] // 开始启动并执行线程
方式二:类方法
[NSThread detachNewThreadSelector: toTarget: withObject: ]
参数同上同名参数;
此方法创建的线程会自动启动执行,无需手动开启。
> 线程方法
//取消线程
- (void)cancel;
//启动线程
- (void)start;
//设置/获取某个线程的状态
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//设置和获取线程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//获取当前线程
+ (NSThread *)currentThread;
//获取主线程
+ (NSThread *)mainThread;
//使当前线程暂停一段时间,或者暂停到某个时刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
// 设置线程优先级
+ (BOOL)setThreadPriority:(double)p;
> 线程间的通信
在非主线程上绝对不能去访问UI, 必须在主线程上更新
[self performSelectorOnMainThread:@selector(updateUI:) withObject:data waitUntilDone:YES];
线程与线程间可进行数据通讯
[self performSelector:@selector(senddata:) onThread:thread2 withObject:obj waitUntilDone:YES]
> 线程同步
多个线程访问同一共享资源时,为保证数据资源的准确性,需对资源进行加锁,是在一个时刻上只有一个线程访问共享资源,其他线程则排队等待。
NSLock 锁
[[NSLock alloc] init]
[theLock lock];
…..要访问的共享资源
[theLock unlock];
二、NSOperation========================================================
1. NSOperation
此类是抽象类不能直接使用。
只能使用系统定义好的子类或者自定义继承NSOperation。
定义好的子类:NSInvocationOperation 和 NSBlockOperation
一个 NSOperation对象 对应一个任务/线程,需要将其放入 NSOperationQueue 任务队列/线程池,才会启动并执行任务/线程。
2. NSInvocationOperation
任务的操作执行在方法中进行
> [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(..) object:nil]
3. NSBlockOperation
任务的操作执行在block中进行
> [NSBlockOperation blockOperationWithBlock:^{
…
}]
4. NSOperationQueue 将任务添加到任务队列中使其启动执行
> NSOperationQueue *que = [[NSOperationQueue alloc] init] // 自定义队列
> que.name // 队列名称
> que.maxConcurrentOperationCount // 最大任务并发数,设置为1时为串行执行任务。默认是-1,表示没有限制,会同时执行队列中的任务。
> [que addOperation:oper1] // 将任务添加到队列中以启动执行
除自定义队列外:
主队列(该队列中的任务在主线程执行):[NSOperationQueue mainQueue]
当前队列:[NSOperationQueue currentQueue]
5. 任务间的依赖
[oper1 addDependency:oper2] // 任务1依赖于任务2,任务1的执行必须等任务2执行完毕后才能开始
注意此操作必须是在将任务添加到队列前执行!!!
6. SDWebImage 图片下载原理运用了NSOperation,NSOperationQueue
三、GCD============================================================
1. GCD:任务,队列
队列分串行,并行队列。
串行队列中的任务执行:GCD会FIFO(先进先出)的取出一个任务,执行一个后,再获取下一个,依次类推。
并行队列中的任务执行:GCD也是按FIFO的方式取出任务,与串行的不同是:取出任务后会放倒别的线程上,然后再取出一个放到另外的线程....。 因为取任务的过程很快,看起来所有任务都是一起执行的。注意:GCD会根据系统资源控制并行的任务数量,任务太多时,不会让所有任务同时执行。
2. 从程序上队列分类:
主队列:(串行队列)dispatch_queue_t qm = dispatch_get_main_queue();
自定义队列:(可串行,并行)dispatch_queue_t qself = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT); // 通过第二个参数指定队列是串行DISPATCH_QUEUE_SERIAL还是并行DISPATCH_QUEUE_CONCURRENT
全局并行队列:dispatch_queue_t qg = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3. 队列中添加要执行的任务(同步[阻塞当前线程],异步任务[不阻塞当前线程])
dispatch_async(qm, ^{
// 在指定的队列中要执行的任务...
});
4. 队列组
多个队列可以放到一个组中。
// 创建队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
// 在指定的队列组中,添加队列queue, 并在queue中添加任务...
});
dispatch_group_notify(group, queue, ^{
// 指定的组里的所有任务完成后,发送通知给指定的队列,并执行一个任务…,通常用于在主队列中更新ui
});