GCD实用篇

快速记忆

gcd在使用上主要是为了多线程操作,及由解决暂用主线程导致UI界面卡顿的问题,其中主要就是同步线程和异步线程。
涉及到的应用方式常用的有如何有效的执行线程等待、多线程协同工作

https://juejin.im/post/5a90de68f265da4e9b592b40#heading-23

// 串行队列的创建方法
dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_SERIAL);
// 并发队列的创建方法
dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_CONCURRENT);

dispatch_get_main_queue() 主队列 串行
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 全局并发队列

dispatch_source_t 定时器相关 set_timer set_event_hander cancel才能停止循环 dispatch_resume启动
dispatch_semaphore_create 信号量 wait等signal 信号,wait可以设置超时时间
dispatch_barrier_async
dispatch_group notiffy等待所有进入group的方法执行完才调用;wait等group执行完才会往下执行;enter leave
dispatch_once
dispatch_after

1. dispatch_semaphore之停车入库原理

1.1 原理

semaphore 信号同步机制,可以简单以停车场停车入库来解释:
停车场有十个车位,现在即使来了十辆车也能全部进来停下,但是此时又来了一辆车,那么他就必须等待库里出来一辆车才行了。
我用普通话解释下上面的代码运行原理。
停车场当前有value个停车位(dispatch_semaphore_create(long value));
此时进入了一辆车(dispatch_semaphore_wait),剩余车位减少一个;
然后又出去一辆车(dispatch_semaphore_signal),剩余车位增加一个;
当剩余车位为0时,再来车(dispatch_semaphore_wait),就只能等待。来的车,等待的时间有两种:
一是给自己设定了一段等待时间(dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 110001000*1000);//等待一秒),这段时间内等不到停车位就走了,如果等到了就开进去停车;
二是一直等下去(DISPATCH_TIME_FOREVER)。
semaphore的三个主要操作函数分别是:

  1. dispatch_semaphore_create 创建一个semaphore
  2. dispatch_semaphore_signal 发送一个信号
  3. dispatch_semaphore_wait 等待信号

1.2 dispatch_semaphore_create

dispatch_semaphore_t  dispatch_semaphore_create(long value); 

传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。
值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。

1.3 dispatch_semaphore_signal

long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

这个函数会使传入的信号量dsema的值加1;

1.4 dispatch_semaphore_wait

long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

这个函数会使传入的信号量dsema的值减1;
这个函数的作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;
如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,
不能直接传入整形或float型数),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,
且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。
如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。

1.5 signal 和 wait

dispatch_semaphore_signal的返回值为long类型,当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。
dispatch_semaphore_wait的返回值也为long型。当其返回0时表示在timeout之前,该函数所处的线程被成功唤醒。当其返回不为0时,表示timeout发生。

1.6 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER

在设置timeout时,比较有用的两个宏:DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER。

  • DISPATCH_TIME_NOW  表示当前;
  • DISPATCH_TIME_FOREVER  表示遥远的未来;
    一般可以直接设置timeout为这两个宏其中的一个,或者自己创建一个dispatch_time_t类型的变量。
    创建dispatch_time_t类型的变量有两种方法,dispatch_time和dispatch_walltime。
    利用创建dispatch_time创建dispatch_time_t类型变量的时候一般也会用到这两个变量。
    dispatch_time的声明如下:
	dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

其参数when需传入一个dispatch_time_t类型的变量,和一个delta值。表示when加delta时间就是timeout的时间。
例如:dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 110001000*1000);
表示当前时间向后延时一秒为timeout的时间。

1.7 代码示例

dispatch_semaphore_t signal;
signal = dispatch_semaphore_create(1);
__block long x = 1;
NSLog(@"0_x:%ld",x);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    NSLog(@"waiting");
    x = dispatch_semaphore_signal(signal);
    NSLog(@"1_x:%ld",x);
        
    sleep(2);
    NSLog(@"waking");
    x = dispatch_semaphore_signal(signal);
    NSLog(@"2_x:%ld",x);
});
//    dispatch_time_t duration = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000); //超时1秒
//    dispatch_semaphore_wait(signal, duration);
    
x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
NSLog(@"3_x:%ld",x);
    
x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
NSLog(@"wait 2");
NSLog(@"4_x:%ld",x);
    
x = dispatch_semaphore_wait(signal, DISPATCH_TIME_FOREVER);
NSLog(@"wait 3");
NSLog(@"5_x:%ld",x);

输出结果为:

2017-08-08 17:14:33.238 Demo[10891:2002129] 0_x:1
2017-08-08 17:14:33.238 Demo[10891:2002129] 3_x:0
2017-08-08 17:14:34.240 Demo[10891:2002953] waiting
2017-08-08 17:14:34.241 Demo[10891:2002953] 1_x:1
2017-08-08 17:14:34.241 Demo[10891:2002129] wait 2
2017-08-08 17:14:34.241 Demo[10891:2002129] 4_x:0
2017-08-08 17:14:36.241 Demo[10891:2002953] waking
2017-08-08 17:14:36.241 Demo[10891:2002953] 2_x:1
2017-08-08 17:14:36.241 Demo[10891:2002129] wait 3
2017-08-08 17:14:36.241 Demo[10891:2002129] 5_x:0


2. dispatch_group之分组执行原理

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t serialQueue = dispatch_queue_create("com.wzb.test.www", DISPATCH_QUEUE_SERIAL);
dispatch_group_enter(group);
dispatch_group_async(group, serialQueue, ^{
    // 网络请求一
    [WebClick getDataSuccess:^(ResponseModel *model) {
        dispatch_group_leave(group);
    } failure:^(NSString *err) {
        dispatch_group_leave(group);
    }];
});
dispatch_group_enter(group);
dispatch_group_async(group, serialQueue, ^{
    // 网络请求二
    [WebClick getDataSuccess:getBigTypeRM onSuccess:^(ResponseModel *model) {
        dispatch_group_leave(group);
    } failure:^(NSString *errorString) {
        dispatch_group_leave(group);
    }];
});
dispatch_group_enter(group);
dispatch_group_async(group, serialQueue, ^{
    // 网络请求三
    [WebClick getDataSuccess:^{
        dispatch_group_leave(group);
    } failure:^(NSString *errorString) {
        dispatch_group_leave(group);
    }];
});
 
// 所有网络请求结束后会来到这个方法
dispatch_group_notify(group, serialQueue, ^{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            // 刷新UI
        });
    });
});

2.1 dispatch_group_create 创建组

dispatch_group_t dispatch_group_create(void);

用于创建任务组

2.2 dispatch_group_enter 进入组

void dispatch_group_enter(dispatch_group_t group);

用于添加对应任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数加1,当未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞和dispatch_group_notify的block执行(下面会讲解wait和notify的不同)

2.3 dispatch_group_leave 离开组

void dispatch_group_leave(dispatch_group_t group);

用于减少任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数减1,dispatch_group_enter和dispatch_group_leave要匹配,不然系统会认为group任务没有执行完毕

2.4 dispatch_group_async 将任务放入执行

void dispatch_group_async(dispatch_group_t group,
                          dispatch_queue_t queue,
                          dispatch_block_t block);

group 对应的任务组,之后可以通过dispatch_group_wait或者dispatch_group_notify监听任务组内任务的执行情况
queue block任务执行的线程队列,任务组内不同任务的队列可以不同
block 执行任务的block

2.5 dispatch_group_wait dispatch_group_notify

  • dispatch_group_wait
    等待组任务完成,会阻塞当前线程,当任务组执行完毕时,才会解除阻塞当前线程
long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

group 需要等待的任务组
timeout 等待的超时时间(即等多久),单位为dispatch_time_t。如果设置为DISPATCH_TIME_FOREVER,则会一直等待(阻塞当前线程),直到任务组执行完毕

  • dispatch_group_notify
    待任务组执行完毕时调用,不会阻塞当前线程
void dispatch_group_notify(dispatch_group_t group,
                           	dispatch_queue_t queue, 
                           	dispatch_block_t block);

group 需要监听的任务组
queue block任务执行的线程队列,和之前group执行的线程队列无关
block 任务组执行完毕时需要执行的任务block

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值