GCD的理解与使用
在这之前先很通俗理解几个基本的概念:
队列(queue):队列是相对任务而言的,队列是一个存放任务的地方。
任务(task):需要做啥?把需要做的事情“打包”成一个任务,再塞入自己所能“掌握”的队列里面。
调度:对列有了,里面也有任务了。那么任务需要在什么时候被开始?
先了解一下队列的四种优先级
/**
* 全局队列的四种优先级
*
* DISPATCH_QUEUE_PRIORITY_HIGH
* DISPATCH_QUEUE_PRIORITY_DEFAULT
* DISPATCH_QUEUE_PRIORITY_LOW
* DISPATCH_QUEUE_PRIORITY_BACKGROUND
* 被设置成后台级别的队列,它会等待所有比它级别高的队列中的任务执行完成或者CPU空闲的时候才会执行自己的任务。
* 例如磁盘的读写操作非常耗时,如果我们不需要立即获取到磁盘的数据,我们可以把读写任务放到后台队列中,这样读写任务只会在恰当的时候去执行,
* 从而让程序更加有效率。
*/
#pragma mark - 只执行一次的函数,多用于单例化
- (void)dispatchOnceToken {
static dispatch_once_t onceToken;
for (NSInteger i = 0; i < 10; i++) {
/// 只会在i == 0的时候进入一次,并且推出再进来,NSLog(@"%ld",i);永远不会再走了
dispatch_once(&onceToken, ^{
NSLog(@"%ld",i);
});
}
}
主要用于单例化。
#pragma mark - 同步和异步
- (void)asycnAction {
weakSelf(self);
/// 异步全局队列,并塞入一个任务
dispatch_async(dispatch_get_global_queue(0, 0), ^{
/// 做一个延迟操作
sleep(5);
/// 回到主线程(主线程默认是一个串行队列,所以这边用sync和async的效果是一样的)
/**
* 这边如果都使用weakself就不会产生强引用,退出当前VC之后立马dealloc
* 但是延迟操作之后,该任务还是会被执行,但是此时weakself为nil,所有任务表现为无效
*
* 如果其中一个使用self,便会产品强引用
*/
dispatch_sync(dispatch_get_main_queue(), ^{
[weakSelf printNum:@"123"];
});
dispatch_async(dispatch_get_main_queue(), ^{
/// 这边使用self会产生强引用,退出当前VC之后并不会立刻dealloc
[self printNum:@"456"];
});
});
}
其实,主线程(main)就是一个串行队列,也是UI的绘制线程,所以保证main线程的通畅是“代码”需要考虑深究的。
#pragma mark - 自定义队列
- (void)customQueue {
/// 串行队列
/// PS:dispatch_queue_attr_t设置成NULL的时候默认代表串行。
dispatch_queue_t serialQueue;
serialQueue = dispatch_queue_create("com.example.SerialQueue", NULL);
/// 并发队列
dispatch_queue_t concurrentQueue;
concurrentQueue = dispatch_queue_create("com.example.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
}
自定义队列,可以在需要手动管理的时候,通过自定义的队列名称,获取到相关的队列,进行挂起等操作。
#pragma mark - 挂起、恢复队列
- (void)suspendAndResumeQueue {
dispatch_queue_t globaleQueue = dispatch_get_global_queue(0, 0);
/// 挂起队列。挂起操作并不会对已经开始执行的任务起作用,仅会阻止队列中还未开始的任务
dispatch_suspend(globaleQueue);
/// 恢复队列
dispatch_resume(globaleQueue);
}
要慎用线程的挂起操作。
#pragma mark - 信号量的使用
/**
* 信号量的作用是控制多个任务对有限数量资源的访问。
* 简单来说就是,如果你创建一个有着两个资源的信号量,那么最多同时只能有两个线程可以访问临界区,其它想使用资源的线程必须在FIFO队列里面等待
*/
- (void)dispatchSemaphores {
weakSelf(self);
/// 创建一个信号量
dispatch_semaphore_t t = dispatch_semaphore_create(1);
dispatch_queue_t concurrentQueue1;
concurrentQueue1 = dispatch_queue_create("com.example.ConcurrentQueue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t concurrentQueue2;
concurrentQueue2 = dispatch_queue_create("com.example.ConcurrentQueue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t concurrentQueue3;
concurrentQueue3 = dispatch_queue_create("com.example.ConcurrentQueue3", DISPATCH_QUEUE_CONCURRENT);
/** 需要在每一个队列里面通过dispatch_semaphore_wait这个来等待信号量*/
dispatch_async(concurrentQueue1, ^{
/// 避免数据的抢夺
dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"第一队列:i=%ld",i);
}
dispatch_async(dispatch_get_main_queue(), ^{
/// 使用完数据,发送一个信号
[weakSelf printNum:@"第一队列任务结束了"];
dispatch_semaphore_signal(t);
});
});
dispatch_async(concurrentQueue2, ^{
dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"第二队列_____:i=%ld",i);
}
dispatch_async(dispatch_get_main_queue(), ^{
/// 使用完数据,发送一个信号
[weakSelf printNum:@"第二队列:任务结束了"];
dispatch_semaphore_signal(t);
});
});
dispatch_async(concurrentQueue3, ^{
dispatch_semaphore_wait(t, DISPATCH_TIME_FOREVER);
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"第三队列——————:i=%ld",i);
}
dispatch_async(dispatch_get_main_queue(), ^{
/// 使用完数据,发送一个信号
[weakSelf printNum:@"第三队列:任务结束了"];
dispatch_semaphore_signal(t);
});
});
}
下面是打印结果:
2017-07-03 15:17:55.109 Functions[13737:1176065] 第一队列:i=0
2017-07-03 15:17:55.109 Functions[13737:1176065] 第一队列:i=1
2017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=2
2017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=3
2017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=4
2017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=5
2017-07-03 15:17:55.110 Functions[13737:1176065] 第一队列:i=6
2017-07-03 15:17:55.111 Functions[13737:1176065] 第一队列:i=7
2017-07-03 15:17:55.111 Functions[13737:1176065] 第一队列:i=8
2017-07-03 15:17:55.111 Functions[13737:1176065] 第一队列:i=9
2017-07-03 15:17:55.126 Functions[13737:1175989] 第一队列任务结束了
2017-07-03 15:17:55.126 Functions[13737:1176068] 第二队列_____:i=0
2017-07-03 15:17:55.126 Functions[13737:1176068] 第二队列_____:i=1
2017-07-03 15:17:55.126 Functions[13737:1176068] 第二队列_____:i=2
2017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=3
2017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=4
2017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=5
2017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=6
2017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=7
2017-07-03 15:17:55.127 Functions[13737:1176068] 第二队列_____:i=8
2017-07-03 15:17:55.128 Functions[13737:1176068] 第二队列_____:i=9
2017-07-03 15:17:55.128 Functions[13737:1175989] 第二队列:任务结束了
2017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=0
2017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=1
2017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=2
2017-07-03 15:17:55.128 Functions[13737:1176107] 第三队列——————:i=3
2017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=4
2017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=5
2017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=6
2017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=7
2017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=8
2017-07-03 15:17:55.129 Functions[13737:1176107] 第三队列——————:i=9
2017-07-03 15:17:55.129 Functions[13737:1175989] 第三队列:任务结束了
可以把每一个i想象成几个线程共享数据的使用,每一次调用都是一次使用,根据打印结果可以看出,不同队列之间,不会在同一时刻同时调用打印i。
其实,可以通过信号量或者干脆点,直接用串行(异步相对于main的串行队列)来避免资源的抢夺。
#pragma mark - Dispatch Groups的使用
/**
* 是一个可以阻塞线程直到一个或多个任务完成的一种方式
* 在那些需要等待任务完成才能执行某个处理的时候,可以使用这个方法
*/
- (void)dispatchGroup {
weakSelf(self);
dispatch_queue_t concurrentQueue1;
concurrentQueue1 = dispatch_queue_create("com.example.ConcurrentQueue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t concurrentQueue2;
concurrentQueue2 = dispatch_queue_create("com.example.ConcurrentQueue2", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t concurrentQueue3;
concurrentQueue3 = dispatch_queue_create("com.example.ConcurrentQueue3", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t groupQueue = dispatch_group_create();
dispatch_group_async(groupQueue, concurrentQueue1, ^{
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"第一队列:i=%ld",i);
}
dispatch_async(dispatch_get_main_queue(), ^{
/// 使用完数据,发送一个信号
[weakSelf printNum:@"第一队列任务结束了"];
});
});
dispatch_group_async(groupQueue, concurrentQueue2, ^{
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"第二队列_____:i=%ld",i);
}
dispatch_async(dispatch_get_main_queue(), ^{
/// 使用完数据,发送一个信号
[weakSelf printNum:@"第二队列:任务结束了"];
});
});
dispatch_group_async(groupQueue, concurrentQueue3, ^{
for (NSInteger i = 0; i < 1000; i++) {
NSLog(@"第三队列——————:i=%ld",i);
}
dispatch_async(dispatch_get_main_queue(), ^{
/// 使用完数据,发送一个信号
[weakSelf printNum:@"第三队列:任务结束了"];
});
});
dispatch_group_async(groupQueue, dispatch_get_global_queue(0, 0), ^{
// dispatch_async(dispatch_get_global_queue(0, 0), ^{
// for (NSInteger i = 0; i < 100000; i++) {
// NSLog(@"全局队列——————:i=%ld",i);
// }
// });
/// 这种情况下,dispatch_group_notify会一直等待它完成,但是如果是上面那种情况,内部再开一个异步并行的,dispatch_group_notify就不会等待,或者说捕捉不到该等待,所以对于网络请求(基本都是异步请求的),是不可以同时放几个请求在group下面,用来等待全部完成的时候refreshUI的
for (NSInteger i = 0; i < 100000; i++) {
NSLog(@"全局队列——————:i=%ld",i);
}
});
dispatch_group_notify(groupQueue, dispatch_get_main_queue(), ^{
NSLog(@"三个任务都完成了");
});
}
对于多个任务,可以开启多个异步并行队列,同时开启多个任务(如果CPU支持的话),用dispatch_group_notify来等待最终的结果。但是不可以在单个队列内部再开启一个异步并行队列(比如UI的刷新同时依赖几个请求的完成),dispatch_group_async是无法捕捉到这样的结束的。
以上就是我对GCD的基本了解以及GCD的常用API,如有错误,希望不吝指正,谢谢!