
「iOS」GCD文章目录「iOS」GCD一、基础知识1. 队列2. 同步、异步二、iOS的多线程方案三、GCD1. 优势2. 使用四、总结一、基础知识1. 队列定义队列又称为伫列queue是先进先出的线性表在具体应用中通常用链表或者数组来实现。装载线程任务的队形结构。队列只允许在后端称为rear进行插入操作在前端称为front进行删除操作。队列的操作方式和堆栈类似唯一的区别在于队列只允许新数据在后端进行添加。**类型**队列的类型决定了任务的执行方式并发、串行队列包括以下几种并发队列 线程执行可以同时一起进行执行,不需要上一个执行完就能执行下一个的。串行队列 线程执行只能依次逐一先后有序的执行,等待上一个执行完再执行下一个。主队列绑定主线程所有任务都在主线程中执行有经过特殊处理的串行队列。全局队列系统提供的并发队列。2. 同步、异步同步sync 只能在当前线程按先后顺序依次执行任务不具备开启新线程的能力。异步async 在新的线程中执行任务具备开启新线程的能力。二、iOS的多线程方案pthread是线程的POSIX标准是一套通用的多线程API可以在Unix/Linux/Windows等平台跨平台使用。iOS中基本不使用。NSThread苹果封装的面向对象的线程类可以直接操作线程比起GCDNSThread效率更高由程序员自行创建当线程中的任务执行完毕后线程会自动退出程序员也可手动管理线程的生命周期。使用频率较低。GCD全称Grand Central Dispatch由C语言实现是苹果为多核的并行运算提出的解决方案CGD会自动利用更多的CPU内核自动管理线程的生命周期程序员只需要告诉GCD需要执行的任务无需编写任何管理线程的代码。GCD也是iOS使用频率最高的多线程技术。NSOperation基于GCD封装的面向对象的多线程技术常配合NSOperationQueue使用使用频率较高。三、GCD1. 优势GCD是苹果公司为多核的并行运算提出的解决方案GCD会自动利用更多的CPU内核比如双核、四核GCD会自动管理线程的生命周期创建线程、调度任务、销毁线程程序员只需要告诉GCD想要执行什么任务不需要编写任何线程管理代码2. 使用同步执行API//queue队列 block任务dispatch_sync(dispatch_queue_t queue,dispatch_block_t block);异步执行//queue队列 block任务dispatch_async(dispatch_queue_t queue,dispatch_block_t block);dispatch_block_t任务无参数、无返回值的 Block 回调用于存放具体需要执行的线程任务底层定义如下typedefvoid(^dispatch_block_t)(void);dispatch_queue_t队列任务存放的队列容器。自定义队列通过dispatch_queue_create创建参数分别为队列名称、队列类型其中串行队列宏DISPATCH_QUEUE_SERIAL等价于 NULL传 NULL 即为串行队列。我们已经知道队列在iOS中主要分为4种自定义的队列都是调用dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr)方法来进行创建两个参数分别为自定义队列名称和队列类型。其中串行队列DISPATCH_QUEUE_SERIAL为宏定义的NULL所以传NULL也表示为串行队列。现在我们来看这2种执行模式3种队列是相互搭配是什么效果同步 (主队列或自定义串行队列)死锁-(void)viewDidLoad{[superviewDidLoad];NSLog(任务1:%,[NSThread currentThread]);dispatch_queue_t queuedispatch_get_main_queue();dispatch_sync(queue,^{NSLog(任务2,[NSThread currentThread]);});NSLog(任务3:%,[NSThread currentThread]);}运行结果打印任务1后死锁崩溃原因分析主队列是串行队列遵循 FIFO 规则。当前viewDidLoad本身是主队列的任务未执行完毕此时通过同步方式向主队列新增任务2同步执行会阻塞主线程等待任务2执行完成。但任务2需要等待viewDidLoad原有任务执行完毕才能执行二者相互等待最终形成死锁。异步 (主队列或自定义串行队列)-(void)viewDidLoad{[superviewDidLoad];NSLog(任务1:%,[NSThread currentThread]);dispatch_queue_t queuedispatch_get_main_queue();dispatch_async(queue,^{NSLog(任务2:%,[NSThread currentThread]);});NSLog(任务3:%,[NSThread currentThread]);}运行结果任务1:_NSMainThread: 0x60000170c000{number 1, name main} 任务3:_NSMainThread: 0x60000170c000{number 1, name main} 任务2:_NSMainThread: 0x60000170c000{number 1, name main}异步执行不会阻塞当前主线程主线程执行完任务1后无需等待任务2完成直接执行任务3。主队列是串行队列所有任务仍在主线程排队执行因此任务3执行完成后再执行任务2不会开启新线程、不会死锁。同步(全局队列或者自定义并发队列) 顺序执行、无新线程-(void)viewDidLoad{[superviewDidLoad];dispatch_queue_t queuedispatch_get_global_queue(0,0);dispatch_sync(queue,^{for(inti0;i3;i){NSLog(任务一%,[NSThread currentThread]);}});dispatch_sync(queue,^{for(inti0;i3;i){NSLog(任务二%,[NSThread currentThread]);}});}运行结果任务一_NSMainThread: 0x600001704000{number 1, name main} 任务一_NSMainThread: 0x600001704000{number 1, name main} 任务一_NSMainThread: 0x600001704000{number 1, name main} 任务二_NSMainThread: 0x600001704000{number 1, name main} 任务二_NSMainThread: 0x600001704000{number 1, name main} 任务二_NSMainThread: 0x600001704000{number 1, name main}同步执行本身不具备开启新线程的能力任务直接在主线程执行且会阻塞线程必须等待上一个任务完成才会执行下一个任务。虽然队列是并发队列但受同步执行特性限制任务只能顺序执行且不会阻塞主线程正常运行。异步(全局队列或者自定义并发队列)并发执行、开启新线程-(void)viewDidLoad{[superviewDidLoad];dispatch_queue_t queuedispatch_get_global_queue(0,0);dispatch_async(queue,^{for(inti0;i3;i){NSLog(任务一%,[NSThread currentThread]);}});dispatch_async(queue,^{for(inti0;i3;i){NSLog(任务二%,[NSThread currentThread]);}});}运行结果任务二NSThread: 0x600001726340{number 6, name (null)} 任务一NSThread: 0x60000170dd00{number 5, name (null)} 任务二NSThread: 0x600001726340{number 6, name (null)} 任务一NSThread: 0x60000170dd00{number 5, name (null)} 任务二NSThread: 0x600001726340{number 6, name (null)} 任务一NSThread: 0x60000170dd00{number 5, name (null)}可以看出任务1和任务2交错执行并非同步执行那样执行完任务1再执行任务2。而且通过线程编号可以看出的确开启了新的线程。说明异步执行具备开启新线程的能力。但是通过上面异步主队列的打印结果我们可以发现在主队列时异步执行也并没有开启新的线程而仍然是同一个主线程。说明如果在主队列中异步执行是不会开启新线程的原因是因为主队列是串行队列必须执行完一个任务再执行另一个任务异步执行只是没有造成堵塞但是在主队列还是是要一步步走的。同一串行队列嵌套-(void)viewDidLoad{[superviewDidLoad];NSLog(任务1);dispatch_queue_t queuedispatch_queue_create(myQueue,DISPATCH_QUEUE_SERIAL);dispatch_async(queue,^{NSLog(任务2);dispatch_sync(queue,^{NSLog(任务3);});NSLog(任务4);});NSLog(任务5);}运行结果任务1 任务5 任务2 然后崩溃这里造成死锁崩溃的原因和上面同步串行队列的原因一样。由于主队列是串行的所以代码必然是从上到下依次执行的。打印完任务1后执行dispatch_async为异步不会阻塞线程但是会加入自定义的串行队列中所以会执行任务5接着执行异步代码块中逻辑打印任务2接着执行dispatch_sync代码块但是由于其是同步串行队列中已经有了dispatch_async任务未执行完毕此时dispatch_sync阻塞当前线程等待任务3执行造成了相互等待所以就死锁崩溃了不同串行队列嵌套-(void)viewDidLoad{[superviewDidLoad];NSLog(任务1);dispatch_queue_t queuedispatch_queue_create(myQueue,DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue2dispatch_queue_create(myQueue2,DISPATCH_QUEUE_SERIAL);dispatch_async(queue,^{NSLog(任务2);dispatch_sync(queue2,^{NSLog(任务3);});NSLog(任务4);});NSLog(任务5);}运行结果任务1任务5任务2任务3任务4当嵌套执行时同步异步在不同的串行队列执行并不会造成死锁崩溃。而是按照串行的顺序执行代码。这事因为dispatch_sync阻塞的并不是当前的线程而是其他的线程所以不会造成死锁等待。同一并发队列嵌套-(void)viewDidLoad{[superviewDidLoad];NSLog(任务1);dispatch_queue_t queuedispatch_queue_create(myQueue,DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue,^{NSLog(任务2);dispatch_sync(queue,^{NSLog(任务3);});NSLog(任务4);});NSLog(任务5);}运行结果任务1任务5任务2任务3任务4主线程执行任务1之后需要异步dispatch_async执行任务2所以先执行主线程的任务5然后执行任务2接着需要在并发队列中同步dispatch_sync执行任务3,所以会造成阻塞等待其执行完成然后执行并发队列中的任务4。不同并发、串行队列嵌套-(void)viewDidLoad{[superviewDidLoad];NSLog(任务1);dispatch_queue_t queuedispatch_queue_create(myQueue,DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue2dispatch_queue_create(myQueue2,DISPATCH_QUEUE_CONCURRENT);dispatch_async(queue,^{NSLog(任务2);dispatch_sync(queue2,^{NSLog(任务3);});NSLog(任务4);});NSLog(任务5);}运行结果任务1任务5任务2任务3任务4创建了新的队列所以是不会造成当前队列的阻塞的。四、总结同步执行sync无新开线程能力阻塞当前线程任务严格顺序执行同一串行队列嵌套同步任务必然触发死锁异步执行async具备新开线程能力不阻塞当前线程在并发队列中任务无序并行执行在串行队列中任务有序排队执行主队列特殊规则无论同步还是异步执行均不会开启新线程同步执行会死锁异步执行仅延迟执行、不崩溃队列嵌套核心同一串行队列嵌套同步任务必死锁不同队列、并发队列嵌套均不会触发死锁