对于太多数人来说,可能都听过,也有可能在项目中用到,对于初学者来说,似乎可能没多大的用户,于是在同步、异步、串行、并行和死锁当中渐渐滴放弃治疗,接下来本文将会彻底长谈GCD。
目录
一、基础理论
1.1 GCD概念
1.2 线程、队列、任务概念
1.3 同步、异步、并发、串行之间的差异
1.4 GCD使用目的
1.5 GCD的优势
1.6 GCD的使用步骤
二、队列任务组合方式
2.1 组合方式
2.2 demo演示
三、结合案例使用
案例1:子线程下载图片->主线程显示图片
案例2:子线程同时执行ABC三个同步任务、全部执行完成再在子线程执行三个同步任务DEF
案例3:售票的小故事(窗口1和窗口2同时售票,售完即止)
一、基础理论
1.1 GCD概念
GCD(Grand Central Dispatch)是基于C的底层的API,完全面向过程,使用简单。
1.2 线程、队列、任务概念
队列遵循先进性先出原则(FIFO),GCD提供的队列有:并发队列、串行队列、全局队列、主队列。
1.3 同步、异步、并发、串行之间的差异
- 同步和异步的区别:能不能开启新的线程
- 并发和串行的区别:任务的执行方式
1.4 GCD使用目的:
GCD最大的目的就是在新的线程中同时执行多个任务,则需满足2个条件:
- 能开启新的线程
- 任务可以同时执行
1.5 GCD的优势
GCD是苹果公司为多核的并行运行提出的解决方案,提供了直接并且简单的调用接口,使用方便
GCD会自动利用更多的CPU内核,更快的内存效率,因为线程栈不暂存于应用内存。
GCD会自动管路线程的生命周期(包括创建线程、调度任务、销毁线程),提供了自动的和全面的线程池管理机制,稳定而便捷
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理的相关代码
1.6 GCD的使用步骤
- 1 定制任务(确定想做的事情)
- 2 将任务添加到队列中
GCD会自动将队列中的任务取出,放到对应的线程中
任务的取出遵循队列的FIFO(先进先出,后进后出)
二、队列任务组合方式
2.1 组合方式
并发队列 + 异步执行 = 多个任务同时执行 + 子线程执行
串行队列 + 异步执行 = 多个任务串行执行 + 子线程执行
全局队列 + 异步执行 = 多个任务同时执行 + 子线程执行
主队列 + 异步执行 = 多个任务串行执行 + 主线程执行
并发队列 + 同步执行 = 多个任务串行执行 + 主线程执行
串行队列 + 同步执行 = 多个任务串行执行 + 主线程执行
主队列 + 同步执行 = 死锁(两个任务处于相互等待状态)
2.2 demo演示
- 并发队列 + 异步执行 = 多个任务同时执行 + 子线程执行 (使用频率较高)
// 并发列队+ 异步执行 :队列中的任务是并发执行,会开启多条线程
/**
DISPATCH_QUEUE_CONCURRENT:并发 0表示并发
DISPATCH_QUEUE_SERIAL:串行
*/
//创建并发队列(指定队列名字+队列类型)
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"-------%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3----%@",[NSThread currentThread]);
});
- 串行队列 + 异步执行 = 多个任务串行执行 + 子线程执行
//串行队列 + 异步执行:队列中的任务是串行执行的,会开线程
//串行:按顺序执行任务
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
NSLog(@"-------%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3----%@",[NSThread currentThread]);
});
- 全局队列 + 异步执行 = 多个任务同时执行 + 子线程执行
//获取全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
NSLog(@"---satrt----");
dispatch_async(queue, ^{
NSLog(@"任务1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3----%@",[NSThread currentThread]);
});
NSLog(@"---end---");
- 主队列 + 异步执行 = 多个任务串行执行 + 主线程执行
//主队列 + 异步执行:所有任务都在主线程中执行,不会开线程
//(特殊串行队列/主队列)
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"---satrt------%@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务1----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务2----%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任务3----%@",[NSThread currentThread]);
});
NSLog(@"---end---");
- 并发队列 + 同步执行 = 多个任务串行执行 + 主线程执行
//并发队列 + 同步执行 : 任务是串行执行的,不会开线程
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"---satrt------%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"任务1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3----%@",[NSThread currentThread]);
});
NSLog(@"end---");
- 串行队列 + 同步执行 = 多个任务串行执行 + 主线程执行
//串行队列 + 同步执行:任务是串行执行的,不会开线程
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
NSLog(@"---satrt------%@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"任务1----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务2----%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任务3----%@",[NSThread currentThread]);
});
NSLog(@"end---");
- 主队列 + 同步执行 = 死锁(两个任务处于相互等待状态)
三、结合案例使用
案例1:针对NSThread中的案例一(子线程下载图片->主线程显示图片)采取GCD方式实现
- (IBAction)demo6 {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"--%@",[NSThread currentThread]);//--<NSThread: 0x60400027b740>{number = 3, name = (null)}
NSURL *url = [NSURL URLWithString:@"http://upload-images.jianshu.io/upload_images/1658521-929b88123cf7156c.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"-main-%@",[NSThread currentThread]); //-main-<NSThread: 0x60000006cc00>{number = 1, name = main}
weakSelf.imgView.image = image;
});
});
}
案例2: 子线程同时执行ABC三个同步任务、全部执行完成再在子线程执行三个同步任务DEF
/**
1.创建并发队列(指定定队列名字+队列类型)
队列类型:
DISPATCH_QUEUE_CONCURRENT(并发队列): 不等待现在执行中的处理是否结束,继续执行下面的处理。只有在异步执行中,才能体现并发性
DISPATCH_QUEUE_SERIAL(串行队列): 等待正在执行中的处理结束,再执行下一条处理。
*/
//
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
//2.任务(同步、异步)
/**
执行任务的方式:
同步 -> 不开启新的线程 dispatch_sync(queue, ^{ ... });
异步 -> 开启新的线程 dispatch_async(queue, ^{ ... });
*/
dispatch_async(queue, ^{
NSLog(@"同步执行->执行任务A---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"同步执行->执行任务B---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"同步执行->执行任务C---%@",[NSThread currentThread]);
});
// dispatch_barrier_sync: 在它前面的任务执行结束后它才执行,在它后面的任务等它执行完成后才会执行
dispatch_barrier_async(queue, ^{
for (int i = 0; i< 1000; i++) {
if (i == 999) {
NSLog(@"----在barrier中添加耗时操作-----%@", [NSThread currentThread]);
}
}
NSLog(@"ABC 全部执行完成之后再在子线程执行三个同步任务DEF");
});
dispatch_async(queue, ^{
NSLog(@"同步执行->执行任务D---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"同步执行->执行任务E---%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"同步执行->执行任务F---%@",[NSThread currentThread]);
});
案例3:售票的小故事(窗口1和窗口2同时售票,售完即止)
//票总数
@property(nonatomic,assign)NSInteger ticketSurplusCount;
self.ticketSurplusCount = 30;
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
/**
信号量:就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。
其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。
dispatch_semaphore_create(信号量值) 创建信号量, 参数:信号量的初值,如果小于0则会返回NULL
dispatch_semaphore_wait(信号量,等待时间) 等待降低信号量
dispatch_semaphore_signal(信号量) 提高信号量
注意:正常的使用顺序是先降低然后再提高,这两个函数通常成对使用
*/
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
while (1) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
if (self.ticketSurplusCount > 0 ) {
self.ticketSurplusCount --;
NSLog(@"剩余票数:%zd 窗口:%@",self.ticketSurplusCount,[NSThread currentThread]);
}
dispatch_semaphore_signal(semaphore);
});
NSLog("%@---############---",[NSThread currentThread]);
if (self.ticketSurplusCount <= 0) {
NSLog(@"不好意思。。票买完了。。。。");
break;
}
}