1.1 什么是GCD
以下摘自苹果官方的说明:
GCD(Grand Central Dispatch)是异步执行任务的技术之一,一般讲应用程序中技记述的线程管理用的代码在系统级中实现,开发者只需要定义想执行的任务,并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。最典型的代码示例:
dispatch_async(queue,^{
//长时间处理的时间
dispatch_async(dispatch_get_main_queue(),^{
//回主线程进行页面更新
});
});
多线程编程实际上是一种极易发生多种问题的编程技术,比如多个线程之间的相同资源的更新,会导致数据的不一致(数据竞争)、停止等待时间的线程会导致多个线程之间的相互持续等待(死锁)、使用太多的线程会导致消耗大量的内存等。
当应用程序启动时,最先执行的线程,即“主线程”来描述用户的界面,处理触摸屏幕的事件,如果在主线程中进行长时间的处理,会妨碍主线程中称为RunLoop的主循环的执行,导致不能及时更新主页面。
1.2 GCD的API
1.2.1 Dispatch Queue
最简单的源代码解析
dispatch_async(queue,^{
/*
该代码的意思是:使用Block语法中“定义想执行的任务”,
通过Dispatch_async函数“追加”赋值在变量queue的“Dispatch_async”
中。
*/
});
Dispatch Queue 存在两种,一种是Serial Dispatch Queue (等待现在执行中处理的队列),另一中是** Concurrent Dispatch Queue**(不等待现在执行中处理的队列)。
1.2.2 dispatch_queue_create
创建代码示例
//**1. Serial Dispatch Queue**
dispatch_queue_t SerialQueue1 = dispatch_queue_create("com.example.SerialQueue1", NULL);
dispatch_queue_t SerialQueue2 = dispatch_queue_create("com.example.SerialQueue2", DISPATCH_QUEUE_SERIAL);
// **2.Concurrent Dispatch Queue**
dispatch_queue_t ConcurrentQueue = dispatch_queue_create("com.example.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
}
Serial Dispatch Queue 同时只能追加一个追加处理,当生成多个串行队列的时候,多个串行队列将并行执行。虽然受到系统资源的限制,但dispatch_queue_create函数可以生成任意多的Dispatch Queue。
生成多个串行队列的优缺点:
缺点:如果过多的使用多线程将会消耗大量的内存,引起大量的上下文切换,大幅度的降低系统的响应性能。
优点:为了避免多线程更新相同资源导致数据竞争时,就需要使用串行队列。
Concurrent Dispatch Queue:可以并行执行多个追加的处理,当想执行不发生数据竞争等问题时,可使用并发队列;
1.2.3 系统提供的队列Main Dispatch Queue和 Global Dispatch Queue
** Main Dispatch Queue :**是在主线程中执行的Dispatch Queue,因为主线程只有一个,所以Main Dispatch Queue 自然是串行队列。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
Global Dispatch Queue:是并发队列,因此没有必要通过create函数逐个生成并发队里额,只要获取Global Dispatch Queue使用即可。全局并发队列有4个执行的优先级,分别是:高、默认、低、后台。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
以下列举主队列和全局队列的一般使用方法
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//处理耗时操作
dispatch_async(dispatch_get_main_queue(), ^{
// 处理UI事件
});
});
关于sync阻塞主线程的问题解释
- (void) test1{
NSLog(@"之前 = %@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"执行事件 = %@",[NSThread currentThread]);
});
NSLog(@"之后 = %@",[NSThread currentThread]);
}
// 打印结果:之前 = <NSThread: 0x600000074240>{number = 1, name = main}
分析:程序首先会打印之前= %@的log,而sync会阻塞当前线程(也就是主线程),按照定义:当执行dispatch_sync的时候,该代码会把block中要执行的任务通过dispatch_sync函数"追加"赋值在变量中,但是当block放进去之后发现该队列正是主队列,而主队列是串行队列,而此时主队列对应的"主线程"被阻塞了,导致block和后面的log都无法打印。
当重新建立一个串行队列取代原来的主队列之后,阻塞就会消失,可以顺利打印结果
- (void) test2{
NSLog(@"之前 = %@",[NSThread currentThread]);
dispatch_sync(dispatch_queue_create("com.example.myqueue", NULL), ^{
NSLog(@"执行事件 = %@",[NSThread currentThread]);
});
NSLog(@"之后 = %@",[NSThread currentThread]);
}
// 打印结果
2017-02-21 14:23:09.263 线程测试[1122:119671] 之前 = <NSThread: 0x60800007d9c0>{number = 1, name = main}
2017-02-21 14:23:09.264 线程测试[1122:119671] 执行事件 = <NSThread: 0x60800007d9c0>{number = 1, name = main}
2017-02-21 14:23:09.264 线程测试[1122:119671] 之后 = <NSThread: 0x60800007d9c0>{number = 1, name = main}
下一个案例:
- (void) test3{
dispatch_queue_t myQueue = dispatch_queue_create("com.example.myqueue" ,NULL);
NSLog(@"之前 = %@",[NSThread currentThread]);
dispatch_async(myQueue, ^{
NSLog(@"执行事件之前 = %@",[NSThread currentThread]);
dispatch_sync(myQueue, ^{
NSLog(@"执行事件 = %@",[NSThread currentThread]);
});
NSLog(@"执行事件之后 = %@",[NSThread currentThread]);
});
NSLog(@"之后 = %@",[NSThread currentThread]);
//2017-02-21 14:30:56.782 线程测试[1144:126020] 之前 = <NSThread: 0x600000075c00>{number = 1, name = main}
//2017-02-21 14:30:56.782 线程测试[1144:126020] 之后 = <NSThread: 0x600000075c00>{number = 1, name = main}
//2017-02-21 14:30:56.782 线程测试[1144:126170] 执行事件之前 = <NSThread: 0x608000260340>{number = 3, name = (null)}
}
分析:log之前、之后都在主线程,async不会阻塞线程,所以主线程会依次打印,执行事件之前的log也会打印,当进入sync的时候,由于sync会阻塞当前线程,而且myQueue是串行队列,每次只能处理一个事件,所生成的线程,所以sync后面block里面的内容不会被执行,当然后面的执行事件之后的log也不会打印。
- (void) test4{
dispatch_queue_t queue = dispatch_queue_create("com.example.myqueue" ,DISPATCH_QUEUE_CONCURRENT);
NSLog(@"之前 = %@",[NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"执行事件之前 = %@",[NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"执行事件 = %@",[NSThread currentThread]);
});
NSLog(@"执行事件之后 = %@",[NSThread currentThread]);
});
NSLog(@"之后 = %@",[NSThread currentThread]);
}
//2017-02-21 14:40:02.657 线程测试[1175:132224] 之前 = <NSThread: 0x60000006de40>{number = 1, name = main}
//2017-02-21 14:40:02.658 线程测试[1175:132224] 之后 = <NSThread: 0x60000006de40>{number = 1, name = main}
//2017-02-21 14:40:02.658 线程测试[1175:132359] 执行事件之前 = <NSThread: 0x608000070640>{number = 3, name = (null)}
//2017-02-21 14:40:02.658 线程测试[1175:132359] 执行事件 = <NSThread: 0x608000070640>{number = 3, name = (null)}
//2017-02-21 14:40:02.658 线程测试[1175:132359] 执行事件之后 = <NSThread: 0x608000070640>{number = 3, name = (null)}
网友评论