dispatch 之 常见函数小结
你好2019!一起努力呀!
直奔主题
1、dispatch_barrier_async VS dispatch_barrier_sync
Barrier blocks only behave specially when submitted to queues created with
* the DISPATCH_QUEUE_CONCURRENT attribute; on such a queue, a barrier block
* will not run until all blocks submitted to the queue earlier have completed,
* and any blocks submitted to the queue after a barrier block will not run
* until the barrier block has completed.
* When submitted to a a global queue or to a queue not created with the
* DISPATCH_QUEUE_CONCURRENT attribute, barrier blocks behave identically to
* blocks submitted with the dispatch_async()/dispatch_sync() API.
NSLog(@"main ---1--");
dispatch_async(self.concurrentQueue, ^{
NSLog(@"test1 begin - ");
sleep();
NSLog(@"test1 - end - ");
});
dispatch_async(self.concurrentQueue, ^{
NSLog(@"test2 begin - ");
sleep();
NSLog(@"test2 - end - ");
});
dispatch_barrier_async(self.concurrentQueue, ^{///分界线在这里 请注意是同步的
NSLog(@"barrier -- start");
sleep();
NSLog(@"barrier -- end");
});
dispatch_async(self.concurrentQueue, ^{
NSLog(@"test4 begin - ");
sleep();
NSLog(@"test4 - end - ");
});
dispatch_async(self.concurrentQueue, ^{
NSLog(@"test5 begin - ");
sleep();
NSLog(@"test5 - end - ");
});
NSLog(@"main ---6--");
示例代码
-- ::42.327067+ HaiFeiArrangeProject[:] main -----
-- ::42.327227+ HaiFeiArrangeProject[:] main -----
-- ::42.327229+ HaiFeiArrangeProject[:] test1 begin -
-- ::42.327253+ HaiFeiArrangeProject[:] test2 begin -
-- ::45.331341+ HaiFeiArrangeProject[:] test1 - end -
-- ::45.331341+ HaiFeiArrangeProject[:] test2 - end -
-- ::45.331612+ HaiFeiArrangeProject[:] barrier -- start
-- ::46.336684+ HaiFeiArrangeProject[:] barrier -- end
-- ::46.336910+ HaiFeiArrangeProject[:] test4 begin -
-- ::46.336911+ HaiFeiArrangeProject[:] test5 begin -
-- ::49.341715+ HaiFeiArrangeProject[:] test5 - end -
-- ::49.341715+ HaiFeiArrangeProject[:] test4 - end -
dispatch_barrier_async 执行结果
-- ::03.909859+ HaiFeiArrangeProject[:] main -----
-- ::03.910086+ HaiFeiArrangeProject[:] test1 begin -
-- ::03.910101+ HaiFeiArrangeProject[:] test2 begin -
-- ::06.913917+ HaiFeiArrangeProject[:] test2 - end -
-- ::06.913964+ HaiFeiArrangeProject[:] test1 - end -
-- ::06.914284+ HaiFeiArrangeProject[:] barrier -- start
-- ::07.915035+ HaiFeiArrangeProject[:] barrier -- end
-- ::07.915219+ HaiFeiArrangeProject[:] main -----
-- ::07.915247+ HaiFeiArrangeProject[:] test4 begin -
-- ::07.915251+ HaiFeiArrangeProject[:] test5 begin -
-- ::10.919249+ HaiFeiArrangeProject[:] test4 - end -
-- ::10.919276+ HaiFeiArrangeProject[:] test5 - end -
dispatch_barrier_sync执行结果
结果分析:
dispatch_barrier_sync(queue,void(^block)())会将queue中barrier前面添加的任务block全部执行后,再执行barrier任务的block,再执行barrier后面添加的任务block,同时阻塞住线程.
dispatch_barrier_async(queue,void(^block)())会将queue中barrier前面添加的任务block只添加不执行,继续添加barrier的block,再添加barrier后面的block,同时不影响主线程(或者操作添加任务的线程)中代码的执行!
简单理解就是:sync 阻塞主线程;async:不阻塞! 参看打印的“main ---6--”!!!
需要注意的:
若将dispatch_barrier加入到global队列中,dispatch_barrier无效
在使用栅栏函数时.使用自定义队列才有意义,如果用的是串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数的作用
2、dispatch_after
DISPATCH_TIME_NOW,表示从现在开始。
DISPATCH_TIME_FOREVER,表示遥远的未来
NSEC:纳秒。
USEC:微妙。
MSEC:毫秒
SEC:秒
PER:每
1s=10的3次方 ms(毫秒)
=10的6次方μs(微秒)
=10v的9次方ns(纳秒)
#define NSEC_PER_SEC 1000000000ull 每秒有多少纳秒
#define NSEC_PER_MSEC 1000000ull 每毫秒有多少纳秒
#define USEC_PER_SEC 1000000ull 每秒有多少微秒。(注意是指在纳秒的基础上)
#define NSEC_PER_USEC 1000ull 每微秒有多少纳秒。
dispatch_after函数并不是延迟对应时间后立即执行block块中的操作,而是将任务追加到对应的队列中,考虑到队列阻塞等情况,所以这个任务从加入队列到真正执行的时间并不准确! 3.0 * NSEC_PER_SEC 表示:3秒
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"执行任务");
});
3、dispatch_once
typedef void (^TestBlock)(void);
TestBlock myTestBlock=^(){
static int count = ;
NSLog(@"count = %d",count ++); };
- (void)dispatchOnceTest
{
/*
dispatch_once 一般多用于单例构造方法中,目前尚未在其他方法中使用过! 关于单例构造的具体实现也不仅仅只有这个还需要重写其他的方法! 之后完善 单例!!!
使用dispatch_once需要注意:其block中的包裹的内容,尽量避免与其他类耦合!
*/
static dispatch_once_t onceToken; dispatch_once(&onceToken, myTestBlock);
dispatch_once(&onceToken, myTestBlock); //虽然执行两次,只有一个输出
/*
2019-01-26 15:37:15.438356+0800 HaiFeiArrangeProject[29785:403238] count = 0
*/
}
4、dispatch_group_notify
//创建队列组
dispatch_group_t group = dispatch_group_create();
NSLog(@"----group--start----"); //封装任务
dispatch_group_async(group, self.concurrentQueue, ^{
sleep();
NSLog(@"1----------%@",[NSThread currentThread]);
}); dispatch_group_async(group, self.concurrentQueue, ^{
sleep();
NSLog(@"2----------%@",[NSThread currentThread]);
}); dispatch_group_async(group, self.concurrentQueue, ^{
sleep();
NSLog(@"3----------%@",[NSThread currentThread]);
}); //4.拦截通知
dispatch_group_notify(group, self.concurrentQueue, ^{
NSLog(@"---dispatch_group_notify------%@",[NSThread currentThread]);
});
//不用等待 队列执行完就会执行这个代码
NSLog(@"----group--end----");
这个代码是 加入到group中的异步操作 这个操作内部是同步的,在这样的情况下 可以如下使用,但是如果异步操作内部也是异步 就需要配合enter和leave实现目前实现的效果! 参看enter 和 leave的操作
5、dispatch_group_leave 和 dispatch_group_leave
dispatch_group_t group =dispatch_group_create();
dispatch_group_enter(group);
//模拟多线程耗时操作
dispatch_group_async(group, self.concurrentQueue, ^{
dispatch_async(self.concurrentQueue, ^{
NSLog(@"1---1--begin");
sleep();
NSLog(@"1---1--end");
dispatch_group_leave(group);
});
});
dispatch_group_enter(group);
//模拟多线程耗时操作
dispatch_group_async(group, self.concurrentQueue, ^{
dispatch_async(self.concurrentQueue, ^{
NSLog(@"2---2--begin");
sleep();
NSLog(@"2--2-end");
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_global_queue(, ), ^{
NSLog(@"%@---全部done。。。",[NSThread currentThread]);
});
NSLog(@"main");
enter 和 leave
-- ::11.860953+ HaiFeiArrangeProject[:] -----begin
-- ::11.860953+ HaiFeiArrangeProject[:] main
-- ::11.860957+ HaiFeiArrangeProject[:] -----begin
-- ::13.861316+ HaiFeiArrangeProject[:] ---end
-- ::14.866069+ HaiFeiArrangeProject[:] -----end
-- ::14.866708+ HaiFeiArrangeProject[:] <NSThread: 0x6000000f0b40>{number = , name = (null)}---全部done。。。
使用后enter和leave的打印输出
-- ::19.111523+ HaiFeiArrangeProject[:] -----begin
-- ::19.111520+ HaiFeiArrangeProject[:] main
-- ::19.111544+ HaiFeiArrangeProject[:] -----begin
-- ::19.111605+ HaiFeiArrangeProject[:] <NSThread: 0x6000019c3600>{number = , name = (null)}---全部done。。。
-- ::21.113975+ HaiFeiArrangeProject[:] ---end
-- ::22.114889+ HaiFeiArrangeProject[:] -----end
注释掉enter和leave之后的打印输出
结论:
1、在加入group的异步操作其内部如果是同步操作,enter和leave加不加均可,若其内部是异步操作,必须使用enter和leave
2、enter 和 leave 必须是成对的出现:若一对enter和leave 只有enter 会导致notify用不执行,如果只有leave,会直接崩溃!
6、dispatch_group_wait
dispatch_group_t group = dispatch_group_create();
//异步
dispatch_group_async(group, self.concurrentQueue, ^{
sleep();
NSLog(@"");
});
dispatch_group_async(group, self.concurrentQueue, ^{
sleep(1.5);
NSLog(@"");
});
dispatch_group_async(group, self.concurrentQueue, ^{
sleep();
NSLog(@"");
});
NSLog(@"aaaaa");
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, * NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == ){
// 属于Dispatch Group的Block全部处理结束
NSLog(@"全部处理结束");
}else{
// 属于Dispatch Group的某一个处理还在执行中
NSLog(@"某一个处理还在执行中");
}
NSLog(@"main");
wait代码示例
-- ::15.450252+ HaiFeiArrangeProject[:] aaaaa
-- ::16.453528+ HaiFeiArrangeProject[:]
-- ::17.451638+ HaiFeiArrangeProject[:] 某一个处理还在执行中
-- ::17.451927+ HaiFeiArrangeProject[:] main
-- ::17.453986+ HaiFeiArrangeProject[:]
-- ::18.453192+ HaiFeiArrangeProject[:]
wait2s的打印输出
-- ::15.072096+ HaiFeiArrangeProject[:] aaaaa
-- ::16.075428+ HaiFeiArrangeProject[:]
-- ::17.076848+ HaiFeiArrangeProject[:]
-- ::18.072394+ HaiFeiArrangeProject[:]
-- ::18.072845+ HaiFeiArrangeProject[:] 全部处理结束
-- ::18.073139+ HaiFeiArrangeProject[:] main
wait5s的打印输出
这里起了3个异步线程放在一个组里,之后通过dispatch_time_t创建了一个超时时间(2秒),程序之后行,立即输出了aaaaa,这是主线程输出的,
当遇到dispatch_group_wait时,主线程会被挂起,等待2秒,在等待的过程当中,子线程分别输出了1和2,2秒时间达到后,主线程发现组里的任务并没有全部结束,然后输出了main。
在这里,如果超时时间设置得比较长(比如5秒),那么会在3秒时第三个任务结束后,立即输出main,也就是说,当组中的任务全部执行完毕时,主线程就不再被阻塞了。
如果希望永久等待下去,时间可以设置为DISPATCH_TIME_FOREVER。
7、dispatch_semaphore_wait
7.1:加锁、
dispatch_semaphore_t semaphore = dispatch_semaphore_create();
for (int i = ; i < ; i++) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//临界区,即待加锁的代码区域
dispatch_semaphore_signal(semaphore);
});
}
在这里,当第一条线程访问临界区时,信号量计数为初始值1,
dispatch_semaphore_wait() 函数判断到计数大于0,于是将计数减1,从而线程允许访问临界区。其它线程因为信号量等于0,就在临界区外等待。
在第一条线程访问完临界区后,这条线程需要发出一个信号,来表明我已经用完临界区的资源了,下个正在等待的线程可以去访问了。
dispatch_semaphore_signal()会将信号量计数加1,就好像发出了一个信号一样,下个在临界区前等待的线程会去接收它。接收到了信号的线程判断到信号量计数大于零了,于是访问临界区。
通过重复这个过程,所有线程都会安全地访问一遍临界区。
可以参考YYKit中的简单的加锁代码
- (instancetype)init {
self = [super init];
_lock = dispatch_semaphore_create();
return self;
}
- (NSURL *)imageURL {
dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
NSURL *imageURL = _imageURL;
dispatch_semaphore_signal(_lock);
return imageURL;
}
YYKit部分源码参考
7.2:异步返回、
- (NSArray *)tasksForKeyPath:(NSString *)keyPath
{
__block NSArray *tasks = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore);
return tasks;
}
这段代码的功能是通过异步的请求取得键路径为 keyPath 的任务数组 tasks,然后返回它。这个方法虽然是异步的,但是执行时间较短。
碰到这种情况,我们肯定最先想到的是用代码块 block 或者代理 delegate 来实现,然后我们就得去声明一个代理,写一个协议方法,或者写一个带有一个参数的代码块,这里AFNetworking巧妙地通过信号量解决了。
我们跟之前的加锁对比,可以发现,信号量在创建时计数是0,
dispatch_semaphore_signal() 函数在 dispatch_semaphore_wait() 函数之前。
AFNetworking 把 dispatch_semaphore_wait() 函数放在返回语句之前,同时信号量计数初始为0,是为了让线程在 tasks 有值之前一直等待。获取 tasks 的异步操作结束之后,这时候 tasks 赋值好了,于是通过 dispatch_semaphore_signal() 函数发出信号,外面的线程就知道不用等待,可以返回 tasks 了。
7.3:控制线程并发数
dispatch_group_t group = dispatch_group_create();
for (int i = ; i < ; i++) {
dispatch_group_async(group, self.concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%d --- 开始 --",i + );
// 线程操作区域 最多有两个线程在此做事情
sleep();
NSLog(@"%d --- end --",i + );
dispatch_semaphore_signal(semaphore);
});
}
// group任务全部执行完毕回调
dispatch_group_notify(group, self.concurrentQueue, ^{
NSLog(@"done");
});
控制并发个数
备注:在应用场景上,限制线程并发数是为了性能考虑,而加锁是为了安全而考虑。
遗留问题:信号量是否线程安全?
文中若有不对之处,还请劳驾之处,谢谢!
信号量部分分析参考自:https://www.jianshu.com/p/de75da4173cf !
dispatch 之 常见函数小结的更多相关文章
- iOS GCD 编程小结
一.简单介绍 1.GCD简介? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD优势 GCD是苹果公司为多核的并行运算提出的 ...
- Maven依赖版本冲突的分析及解决小结
1:前言 做软件开发这几年遇到了许多的问题,也总结了一些问题的解决之道,之后慢慢的再遇到的都是一些重复性的问题了,当然,还有一些自己没有完全弄明白的问题.如果做的事情是重复的,遇到重复性问题的概率也就 ...
- Vue学习小结(一)安装依赖与数据来源
不多说啥了,生活中都是各种阵痛与惊喜.最近在学习vue框架,刚写完一个小型的后台管理系统(https://github.com/michaelzhengzm/info-manager-systerm_ ...
- day66 模板小结 [母板继承,块,组件]
小结: day65 1. 老师编辑功能写完 1. magic2函数 --> 用两层for循环解决 全栈8期之殇 问题 2. 模板语言 in 语法 {% if xx in xx_list %} { ...
- React及Nextjs相关知识点小结
React及Nextjs知识点小结 函数式组件和类组件区别是什么 1.函数式组件是用于创建无状态的组件,组件不会被实例化,无法访问this中的对象,无法访问生命周期方法,是无副作用的,相比于类组件函数 ...
- 对MYSQL注入相关内容及部分Trick的归类小结
前言 最近在给学校的社团成员进行web安全方面的培训,由于在mysql注入这一块知识点挺杂的,入门容易,精通较难,网上相对比较全的资料也比较少,大多都是一个比较散的知识点,所以我打算将我在学习过程中遇 ...
- 从零开始编写自己的C#框架(26)——小结
一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...
- iOS开发之再探多线程编程:Grand Central Dispatch详解
Swift3.0相关代码已在github上更新.之前关于iOS开发多线程的内容发布过一篇博客,其中介绍了NSThread.操作队列以及GCD,介绍的不够深入.今天就以GCD为主题来全面的总结一下GCD ...
- Python自然语言处理工具小结
Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...
随机推荐
- Git简介、安装与配置
老规矩QAQ,先来简单介绍一下Git: Git是一个分布式版本控制系统,可以理解为是一个用于管理代码,控制版本,方便多人合作开发的一款工具. Git:分布式版本控制系统. SVN.CVS:集中式版本控 ...
- Java中的数据类型转换
先来看一个题: Java类Demo中存在方法func0.func1.func2.func3和func4,请问该方法中,哪些是不合法的定义?( ) public class Demo{ float fu ...
- Android深入四大组件(六)Service的启动过程
前言 此前我用较长的篇幅来介绍Android应用程序的启动过程(根Activity的启动过程),这一篇我们接着来分析Service的启动过程.建议阅读此篇文章前,请先阅读Android深入四大组件(一 ...
- Project Euler 44: Find the smallest pair of pentagonal numbers whose sum and difference is pentagonal.
In Problem 42 we dealt with triangular problems, in Problem 44 of Project Euler we deal with pentago ...
- Python 爬虫练习项目——异步加载爬取
项目代码 from bs4 import BeautifulSoup import requests url_prefix = 'https://knewone.com/discover?page=' ...
- laravel入门-01
创建laravel应用 laravel new app_name 使用 PHP 内置 web server 驱动我们的网站 cd xxx/public php -S localhost:port 查看 ...
- exchange 普通用户可以创建通讯组
运维发现,通讯组多了好多未知名称的,经查为普通用户通过owa新建的,怎么阻止用户新建通讯组呢. 在搭建exchange后,系统会自动创建一个“Default Role Assignment Polic ...
- RESTful的理解与设计【PHP】
RESTful 就是一种软件架构的风格,以资源为中心定位,运用http的请求方式(动词)来划定操作.这样的设定优点简单易理解,方便人员对接,形成规范. 资源作为唯一标识,使用相关动词取获取操作.举例, ...
- 异常处理与MiniDump详解(1) C++异常(转)
异常处理与MiniDump详解(1) C++异常 write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie 讨论新闻组及文件 一. 综述 我很少敢为自己写 ...
- select poll epoll Linux高并发网络编程模型
0 发展历程 同步阻塞迭代模型-->多进程并发模型-->多线程并发模型-->select-->poll-->epoll-->... 1 同步阻塞迭代模型 bind( ...