前言
iOS开发之多线程理论部分
NSOperation
NSThread
什么是GCD
全称是Grand Central Dispath,纯C语言,提供非常多强大的函数,是目前苹果官网推荐的多线程开发方法,NSOperation便是基于GCD的封装。
GCD的优势所在:
1. 为多核的并行运算提出了解决方案
2. GCD会自动利用更多的CPU内核,比如双核,四核。
3. GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
4. 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD中有2个核心概念
任务:执行什么操作
队列:用来存放任务
队列可以分为两大类型
串行队列(Serial Dispatch Queue):只有一个线程,加入到队列中的操作按添加顺序依次执行,一个任务执行完毕后,才能再执行下一个任务。
并发队列(Concurrent Dispatch Queue):有多个线程,操作进来以后他会将这些线程安排在可用的处理器上,同时保证先进来的任务优先处理。
其实在GCD中还有一个特殊队列就是主队列,用来执行主线程上的操作任务
GCD其实可以抽象为两步
//这是找到了更新UI的主线程所在的队列
dispatch_queue_t mainQueue= dispatch_get_main_queue();
/*
* 创建一个队列
* 第一个参数:队列名字
* 第二个参数:队列类类型
并行队列:DISPATCH_QUEUE_CONCURRENT
串行队列:DISPATCH_QUEUE_SERIAL
*
*/
dispatch_queue_t serialQueue = dispatch_queue_create("mySerialQueue", DISPATCH_QUEUE_SERIAL);
/*
* 系统内部给我们提供有一个现成的并发队列
* 第一个参数:线程的优先级, DISPATCH_QUEUE_PRIORITY_BACKGROUND是最低的。
* 第二个参数:系统保留的参数,永远传0
*/
dispatch_queue_t queue = dispatch_get_global_queue(0 , 0);
2. 在队列中确定想做的事
//在找到的队列中确定想要做的事情,这里我采用的同步方式执行任务,asyn是异步。
dispatch_sync(mainQueue, ^{
});
执行队列中任务的两种方式
用同步的方式执行任务(同步:synchronization),只能在当前线程中执行任务,不具备开启新线程的能力
/*
* 第一个参数:该任务所在的队列
* 第二个参数:该任务要做的事情
*/
dispatch_sync(dispatch_queue_tqueue,dispatch_block_tblock);
假如我指定的队列A是串行队列,则该队列中只能有一个线程,也就是说我放在队列A中的任务,所以必须得一个一个的执行。不仅如此,在上面我们还手动选择了在队列A中用同步的方式执行任务,这也限制了,队列中的任务只能一个一个执行。
假如我指定的队列A是并行队列,则该队列中可以开辟多个线程去执行任务,虽然如此,但由于我们在上面手动选择了在队列A中用同步的方式执行线程,所以队列A中的任务也只能一个一个去执行,不能开辟多线程同时执行。
2. 用异步的方式执行任务(异步:asynchronous),可以在新的线程中执行任务,具备开启新线程的能力。
dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);
假如此时我指定的队列B是并行队列,则表明该队列中可以存在多个线程,又因为我们采用的是异步的方式执行任务,所以在这个队列的任务可以实现同时运行。
假如此时我指定的队列B是串行队列,则表明该队列中,只能有一个线程,所以尽管我采用异步的方式执行任务,但该队列中的任务还是只能一个一个的运行。
GCD创建的线程任务有四种执行方式
1.串行队列同步执行任务
同步不具有开辟新线程的能力,不会开辟新的线程去执行任务,会在当前程序的主线程中执行任务。
按照串行的方式去执行任务
#pragma mark ------------------串行队列同步执行任务--------------dispatch_queue_tserialQueue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"-%@",[NSThreadcurrentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"1 - %@", [NSThreadcurrentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"2 - %@", [NSThreadcurrentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"3 - %@", [NSThreadcurrentThread]);
});
打印结果
2016-03-01 11:44:15.071 GCD[22196:2030750] 1 = {number = 1, name = main}
2016-03-01 11:44:15.071 GCD[22196:2030750] 2 = {number = 1, name = main}
2016-03-01 11:44:15.071 GCD[22196:2030750] 3 = {number = 1, name = main}
2016-03-01 11:44:15.071 GCD[22196:2030750] 4 {number = 1, name = main}
由于是同步操作,不能开辟线程,所以都是在主线程并按照顺序执行
2. 串行队列异步执行任务
异步具有创建新线程的能力,会开辟新的线程去执行任务
按照串行的方式去执行任务
#pragma mark ------------------串行队列异步执行任务------------dispatch_queue_tserialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue1, ^{
NSLog(@"1 = %@",[NSThreadcurrentThread]);
});
dispatch_async(serialQueue1, ^{
NSLog(@"2 = %@",[NSThreadcurrentThread]);
});
dispatch_async(serialQueue1, ^{
NSLog(@"3 = %@",[NSThreadcurrentThread]);
});
打印结果
2016-03-01 11:56:04.116 GCD[22310:2039747] 1 = {number = 2, name = (null)}
2016-03-01 11:56:04.117 GCD[22310:2039747] 2 = {number = 2, name = (null)}
2016-03-01 11:56:04.118 GCD[22310:2039747] 3 = {number = 2, name = (null)}
因为是异步操作,所以有个编号为2的子线程被开辟,但有因为是串行队列,所以只开辟了一个线程。最终造就了三个线程顺序执行。
3. 并行队列同步执行任务
同步不具有创建新线程的能力,不会开辟新的线程去执行任务,会在当前程序的主线程去执行任务
按照同步的方式去执行任务
虽然并行队列决定了该队列中可以有多个线程,但由于是同步操作,不能开辟线程,所以还都是在主线程中按顺序执行。
4. 并发队列异步执行任务(常用)
异步具有创建新线程的能力,会开辟新的线程去执行任务,不会在当前程序的主线程去执行任务
按照并发的方式去执行任务
dispatch_queue_tconcurrentQueue1 = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue1, ^{
NSLog(@"1 = %@",[NSThreadcurrentThread]);
});
dispatch_async(concurrentQueue1, ^{
NSLog(@"2 = %@",[NSThreadcurrentThread]);
});
dispatch_async(concurrentQueue1, ^{
NSLog(@"3 = %@",[NSThreadcurrentThread]);
});
打印结果
2016-03-01 14:32:35.880 GCD[22990:2122064] 2 = {number = 3, name = (null)}
2016-03-01 14:32:35.880 GCD[22990:2122063] 1 = {number = 2, name = (null)}
2016-03-01 14:32:35.880 GCD[22990:2122065] 3 = {number = 4, name = (null)}
并行队列可以里可以有多个线程,同步执行的方式又可以开辟多个线程,所以这里实现了多个线程并行执行。
线程锁
没线程锁的情况下:我走进购票大厅,买票的人都没有排队,我好不容易挤到窗口前,正打算掏钱买票的时候,旁边有人已经把钱给了售票员。虽然你的线程已经开始执行买票的方法,但当你去拿票时,也就是将票数减一时,CPU将你的线程给中断,开始执行其他的线程,CPU返回继续执行你的线程的时候,票已经没了。
有线程锁的情况下:*我走进购票大厅,买票的人都在排队,当我到柜台能保证我买票的关键过程,也就是报站、掏钱、拿票过程不受干扰,我采用线程锁将这个关键过程给锁起来,以保证我能顺利的买到票。