你好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 之 常见函数小结的更多相关文章

  1. iOS GCD 编程小结

    一.简单介绍 1.GCD简介? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD优势 GCD是苹果公司为多核的并行运算提出的 ...

  2. Maven依赖版本冲突的分析及解决小结

    1:前言 做软件开发这几年遇到了许多的问题,也总结了一些问题的解决之道,之后慢慢的再遇到的都是一些重复性的问题了,当然,还有一些自己没有完全弄明白的问题.如果做的事情是重复的,遇到重复性问题的概率也就 ...

  3. Vue学习小结(一)安装依赖与数据来源

    不多说啥了,生活中都是各种阵痛与惊喜.最近在学习vue框架,刚写完一个小型的后台管理系统(https://github.com/michaelzhengzm/info-manager-systerm_ ...

  4. day66 模板小结 [母板继承,块,组件]

    小结: day65 1. 老师编辑功能写完 1. magic2函数 --> 用两层for循环解决 全栈8期之殇 问题 2. 模板语言 in 语法 {% if xx in xx_list %} { ...

  5. React及Nextjs相关知识点小结

    React及Nextjs知识点小结 函数式组件和类组件区别是什么 1.函数式组件是用于创建无状态的组件,组件不会被实例化,无法访问this中的对象,无法访问生命周期方法,是无副作用的,相比于类组件函数 ...

  6. 对MYSQL注入相关内容及部分Trick的归类小结

    前言 最近在给学校的社团成员进行web安全方面的培训,由于在mysql注入这一块知识点挺杂的,入门容易,精通较难,网上相对比较全的资料也比较少,大多都是一个比较散的知识点,所以我打算将我在学习过程中遇 ...

  7. 从零开始编写自己的C#框架(26)——小结

    一直想写个总结,不过实在太忙了,所以一直拖啊拖啊,拖到现在,不过也好,有了这段时间的沉淀,发现自己又有了小小的进步.哈哈...... 原想框架开发的相关开发步骤.文档.代码.功能.部署等都简单的讲过了 ...

  8. iOS开发之再探多线程编程:Grand Central Dispatch详解

    Swift3.0相关代码已在github上更新.之前关于iOS开发多线程的内容发布过一篇博客,其中介绍了NSThread.操作队列以及GCD,介绍的不够深入.今天就以GCD为主题来全面的总结一下GCD ...

  9. Python自然语言处理工具小结

    Python自然语言处理工具小结 作者:白宁超 2016年11月21日21:45:26 目录 [Python NLP]干货!详述Python NLTK下如何使用stanford NLP工具包(1) [ ...

随机推荐

  1. 苹果ios,下拉菜单错位的问题(目前iphone x没发现有这个问题)

    苹果手机,点击下拉框,再点击确认按钮,页面位置错乱(感觉背景整体往上移动了一段距离,并且,页面所有的元素都往上移了一定的距离),导致手机页面底部留白的问题,并且,元素实际位置跟页面位置不一致. 解决方 ...

  2. attr()与prop()之全选、反选问题

    获取js dom原生属性的时候最好用prop()方法,获取自己添加的属性时用attr() 原文:http://blog.sina.com.cn/s/blog_bf5ce8cc0102vuyt.html ...

  3. C++学习笔记(3)----类模板的static成员

    与任何其他类相同,类模板可以声明 static 成员: template <typename T> class Foo { public: static std::size_t count ...

  4. apm飞控飞行模式详解

    1.稳定模式Stabilize稳定模式是使用得最多的飞行模式,也是最基本的飞行模式,起飞和降落都应该使用此模式.此模式下,飞控会让飞行器保持稳定,是初学者进行一般飞行的首选,也是FPV第一视角飞行的最 ...

  5. C++ 多线程编程实例【2个线程模拟卖火车票的小程序】

    原文:http://blog.csdn.net/chen825919148/article/details/7904219 核心提示:从网上搜集来的非常基础的C++多线程实例,刚入门的可以看看,希望能 ...

  6. java 策略模式

    <Head First 设计模式>学习中  设计原则 找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起 针对接口编程,而不是针对实现编程 多用组合少用继承   ...

  7. 如何使用chrome devtool调试Mobile网页?

    凡是做过mobile网页web app开发的朋友一定对开发效率的底下会有吐槽.现在chrome dev tool改变了程序员们的苦比. 0.登录google chrome 1. chrome://in ...

  8. Python实例---三级菜单的实现[high]

    # version: python3.2.5 # author: 'FTL1012' # time: 2017/12/7 09:16 menu = { '陕西': { '西安': { '未名区': [ ...

  9. August 19th 2017 Week 33rd Saturday

    Live for today and stop stressing out about tomorrow. 为今天而活,不必为明天过分担心. Stop bewailing the past misfo ...

  10. August 15th 2017 Week 33rd Tuesday

    Would rather have done a regret, do not miss the regret. 宁愿做过了后悔,也不要错过了后悔. Yesterday, I read several ...