http://blog.csdn.net/victormokai/article/details/42419483
基本每门语言都有多线程这个技术点,多线程是为了实现并发执行,可以理解为一个系统进程是由一个或多个线程组成的。iOS中创建线程的方式简单到可以直接调用对象的方法来实现,下面我们来看看。
调用NSObject方法实现多线程
NSObject提供了以 performSelector为前缀的一系列方法。它允许用户在指定的线程、什么时间执行某个方法的调用。实现了多线程编程最简洁的方式
在当前线程中执行方法
在主线程中执行方法在指定线程中执行方法
在后台线程中执行方法
取消延迟调用的方法
上述方法中需要注意的是
指定执行的方法(但传入的参数数量有限制,最多两个);
iOS默认的主线程为UI线程,更新UI的代码需要在主线程中执行,这点与Android相同,一般在非主线程需要进行UI更新,可以调用performSelectorOnMainThread来实现;
如果指定在后台线程执行,则会自动创建一个线程去调用方法
如果使用了waitUntilDone:相关的方法来延迟调用方法,在期间如果不想调用了可以取消掉
NSThread
以对象的方式来创建线程,它是线程的一个轻量级实现,有点类似于Java的Thread类。在执行一些简单任务时,NSThread很有用,比如线程休眠、线程同步、线程间通信
三种创建方式
立即执行
通过 start执行
通过继承方式
相关方法
开启线程 - (void)start;
停止线程
- (void)cancel;
+ (void)exit;与前者的区别在于,它是强制性的,不会给你线程清理任何资源的机会
休眠线程
+ (void)sleepUntilDate:(NSDate*)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
获取主线程 [NSThread mainThread];
判断当前线程是否为主线程 [NSThread isMainThread]
获取当前线程 [NSThread currentThread];
线程间同步
涉及到线程间同步仍然需要配合使用NSLock,NSCondition 或者 @synchronized。
一个简单例子,两个人同时一起抢火车票,但是票只有最后一张了,看谁先抢到,当然这里的抢票规则完全靠哪个线程被先执行谁就先拿到票,话说我现在都还没抢到高铁票,过年又得骑车回家啦
上述run方法也可以用@synchronized来代替
传进去的self为对象锁,可以理解为该对象做了一个标识“这个对象我在用,你等着!我用完你再用。”
好了,多运行多几次,控制台会随机的输出:
2015-01-06 11:38:18.119 TestMultithreading[1266:77053] mokai抢到一张火车票
2015-01-06 11:38:26.028 TestMultithreading[1269:77140] mokai抢到一张火车票
2015-01-06 11:38:32.259 TestMultithreading[1273:77236] mokai抢到一张火车票
2015-01-06 11:38:38.450 TestMultithreading[1276:77311]gongkai抢到一张火车票
GCD
参考:iOS多线程GCD
GCD(GrandCentral Dispatch),基于系统多核编程、C函数式编程、结合Blocks配合使用
简单一例
在上面我们可以通过performSelector系列方法来创建线程
在GCD中,我们是这样写的
虽然看起来没前者那么简洁,尤其对于面向对象开发者来说,函数式编程怎么看怎么不爽。但是官方说了, GCD 是基于多核的,性能杆杆的,强烈建议能用 GCD 解决尽量使用 GCD 解决。不管你用不用,反正我平常都是用的本文第一种“调用NSObject方法实现多线程”分发队列
执行任务的容器,可以提供各种自定义操作,如并行,串行
在GCD中有三种队列:
1、主线程队列
主线程队列中的任务会在应用的主线程中执行。一般用于UI更新操作
dispatch_queue_t queue =dispatch_get_main_queue();
2、并行队列
串行分发队列又被称为全局分发队列,由系统创建三个不同优先级的dispatchqueue,也按顺序执行队列中的任务,但是顺序开始的多个任务会并发同时执行。并发分发队列常用于管理并发任务。
dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
3、串行队列
串行分发队列又被称为私有分发队列,按顺序执行队列中的任务,且同一时间只执行一个任务。但各个串行队列之间是并发的。
当想要任务按照某一个特定的顺序执行时,串行队列是很有用的。串行队列在同一个时间只执行一个任务。我们可以使用串行队列代替锁去保护共享的数据。和锁不同,一个串行队列可以保证任务在一个可预知的顺序下执行。另外,通过这种方式创建的队列需要我们手动调用dispatch_release释放
dispatch_queue_t queue =dispatch_queue_create("me.mokai.queue",NULL);//注意,此处传的是C字符不是@""
任务执行
同步执行,主线程会被阻塞,可以理解为Block的执行是主线程上进行的
dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);
异步执行,与同步执行相反,主线程不会被阻塞
dispatch_async(dispatch_queue_t queue,dispatch_block_t block);
加载网络图片的例子
实际中我们会使用了嵌套GCD来完成功能,先用后台线程执行任务,然后调用主线程执行UI更新,注意,GCD的嵌套也不能乱嵌,否则会出现死锁情况,像如下代码,会立即死锁
常见场景
1、在主线程执行代码
2 、延时执行 3、使用 dispatch_once 实现线程安全单一执行要求GCD 的 dispatch_once能够在保证自应用运行到暂停 block只执行一次。以下是编程中经常用到的单例模式,这也是现在OC中推荐的一种方法
操作队列NSOperation
参考:iOS多线程编程之NSOperation和NSOperationQueue的使用
NSOperation,类似于java的Runable接口,设计用来扩展的,自带有二个扩展,NSInvocationOperation(基于Selector调用)、NSBlockOperation(基于Block调用),如果想自定义只需重写main方法即可(java的run方法),使用与前面介绍的NSThread差不多NSOperationQueue队列,用来管理执行Operations的容器
NSInvocationOperation
NSBlockOperation
细粒化控制
通过NSOperation与NSOperationQueue的配合使用,可实现复杂的流程化任务。
1、NSOperation依赖对象,通过addDependency来添加一个或者多个依赖的对象,只有所有依赖的对象都已经完成操作,当前NSOperation对象才会开始执行操作。另外,通过removeDependency方法来删除依赖对象。依赖关系不局限于相同queue中的NSOperation对象,NSOperation对象会管理自己的依赖, 因此完全可以在不同的queue之间的NSOperation对象创建依赖关系
唯一的限制是不能创建环形依赖,比如A依赖B,B依赖A,这是错误的
依赖关系会影响到NSOperation对象在queue中的执行顺序,默认是按照添加顺序执行的
2、执行修改Operations的执行顺序
依赖关系相对优先级:优先级等级则是operation对象本身的一个属性。默认所有operation都拥有“普通”优先级,不过可以通过setQueuePriority:方法来提升或降低operation对象的优先级。优先级只能应用于相同queue中的operations。如果应用有多个operationqueue,每个queue的优先级等级是互相独立的。因此不同queue中的低优先级操作仍然可能比高优先级操作更早执行。
3、设置队列的最大并发操作数量
setMaxConcurrentOperationCount:默认为
如果设置为1就表示为串行化operationqueue
4、等待Options完成
5、暂停和继续queue
重复任务NSTimer、NSRunLoop
有时应用中需要用到定时刷新的功能,如抢票APP需要频繁地请求12306,用之前的GCD也能实现效果,但是过程太痛苦了,那么苹果提供了NSTimer类来帮助我们完成重复任务。
NSTimer与NSOperation设计一样,可以通过自身来启动任务,也可以放到NSRunLoop里面执行
通过这种方式创建的NSTimer结果只会调用一次refresh方法,无论repeats是YES还是NO都会如此,如果想重复执行则需要添加到NSRunLoop中,如下
这里的代码每隔1秒就会调用一次log方法,其实NSTimer提供了一个快捷方法,上述代码也可以写成
停止任务可以通过[timer invalidate]来执行,需要注意的是NSTimer没有提供暂停的接口,如果想实现暂停的功能可以先invalidate然后重新启动一个NSTimer