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) [ ...
随机推荐
- Android学习笔记(3)----手机调试[OFFLINE]的解决方式
问题描述 今天用Android Studio开发了一个简单的调用摄像头的App,结果想调试的时候发现选择调试设备的对话框中,手机名称后面总是跟着一个[OFFLINE]的标识,只能选择启动AVD来进行调 ...
- MAVLink Linux/QNX/MacOs Integration Tutorial (UDP)
MAVLink Linux/QNX/MacOs Integration Tutorial (UDP) Overview This program was written to test the udp ...
- Two ways to assign values to member variables
setXxx()方法,带参数的构造方法.类名作为形式参数,其实里面需要传入一个该类的对象.类名作为返回值,其实返回的是一个该类的对象.
- Raspberry install wine
sudo apt install wine winecfg出现问题 树莓派3B是卡片电脑,内存为1GB,一般运行Linux.Linux两种主流的内存分配方法2G/2G和3G/1G,树莓派系统后期优化性 ...
- bootstrap-table 分页增删改查之一(增加 删除)
先上效果图 引入js文件 <!--js jquery --> <script type="text/javascript" src="${pageCon ...
- windows&cmd常用命令&快捷键
1: cls即CLear Screen 功能:清除屏幕上的所有显示 2:Win+Tab快捷键进行3D窗口切换还有比较快速切换窗口的新方法. 3:利用Alt+Tab快捷键进行切换窗口时,在桌面中间会显示 ...
- 毕向东_Java基础视频教程第19天_IO流(01~05)
第19天-01-IO流(BufferedWriter) 字符流的缓冲区 缓冲区的出现提高了对数据的读写效率. 对应类缓冲区要结合流才可以使用. BufferedWriter BufferedReade ...
- .net core系列之《.net平台历程介绍以及.net framework和.net core对比》
一..Net平台的背景 1.2010之前 的PC时代的时候,互联网规模还不是特别庞大,以静态编译式语言为代表的JAVA和.Net没什么太大区别,.net以windows自居. 2.2010年以JAVA ...
- FTP列出文件列表
#定义FTP服务器地址$ftpURL = "ftp://192.168.12.6/"#定义登录FTP服务器的账户及密码$username = "testj\adadmin ...
- December 10th 2016 Week 50th Saturday
Storms make trees take deeper roots. 风暴使树木深深扎根. Sometimes, you may feel frustrated for failing to wi ...