博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dispatch 之 常见函数小结
阅读量:5908 次
发布时间:2019-06-19

本文共 13258 字,大约阅读时间需要 44 分钟。

你好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(3);        NSLog(@"test1 - end - ");    });    dispatch_async(self.concurrentQueue, ^{        NSLog(@"test2 begin - ");                sleep(3);        NSLog(@"test2 - end - ");            });    dispatch_barrier_async(self.concurrentQueue, ^{
///分界线在这里 请注意是同步的 NSLog(@"barrier -- start"); sleep(1); NSLog(@"barrier -- end"); }); dispatch_async(self.concurrentQueue, ^{ NSLog(@"test4 begin - "); sleep(3); NSLog(@"test4 - end - "); }); dispatch_async(self.concurrentQueue, ^{ NSLog(@"test5 begin - "); sleep(3); NSLog(@"test5 - end - "); }); NSLog(@"main ---6--");
示例代码
2019-01-26 14:10:42.327067+0800 HaiFeiArrangeProject[28551:342894] main ---1--     2019-01-26 14:10:42.327227+0800 HaiFeiArrangeProject[28551:342894] main ---6--     2019-01-26 14:10:42.327229+0800 HaiFeiArrangeProject[28551:342934] test1 begin -     2019-01-26 14:10:42.327253+0800 HaiFeiArrangeProject[28551:342935] test2 begin -     2019-01-26 14:10:45.331341+0800 HaiFeiArrangeProject[28551:342934] test1 - end -     2019-01-26 14:10:45.331341+0800 HaiFeiArrangeProject[28551:342935] test2 - end -     2019-01-26 14:10:45.331612+0800 HaiFeiArrangeProject[28551:342935] barrier -- start     2019-01-26 14:10:46.336684+0800 HaiFeiArrangeProject[28551:342935] barrier -- end     2019-01-26 14:10:46.336910+0800 HaiFeiArrangeProject[28551:342935] test4 begin -     2019-01-26 14:10:46.336911+0800 HaiFeiArrangeProject[28551:342934] test5 begin -     2019-01-26 14:10:49.341715+0800 HaiFeiArrangeProject[28551:342934] test5 - end -     2019-01-26 14:10:49.341715+0800 HaiFeiArrangeProject[28551:342935] test4 - end -
dispatch_barrier_async 执行结果
2019-01-26 14:10:03.909859+0800 HaiFeiArrangeProject[28531:342041] main ---1--     2019-01-26 14:10:03.910086+0800 HaiFeiArrangeProject[28531:342080] test1 begin -     2019-01-26 14:10:03.910101+0800 HaiFeiArrangeProject[28531:342081] test2 begin -     2019-01-26 14:10:06.913917+0800 HaiFeiArrangeProject[28531:342081] test2 - end -     2019-01-26 14:10:06.913964+0800 HaiFeiArrangeProject[28531:342080] test1 - end -     2019-01-26 14:10:06.914284+0800 HaiFeiArrangeProject[28531:342041] barrier -- start     2019-01-26 14:10:07.915035+0800 HaiFeiArrangeProject[28531:342041] barrier -- end     2019-01-26 14:10:07.915219+0800 HaiFeiArrangeProject[28531:342041] main ---6--     2019-01-26 14:10:07.915247+0800 HaiFeiArrangeProject[28531:342081] test4 begin -     2019-01-26 14:10:07.915251+0800 HaiFeiArrangeProject[28531:342082] test5 begin -     2019-01-26 14:10:10.919249+0800 HaiFeiArrangeProject[28531:342081] test4 - end -     2019-01-26 14:10:10.919276+0800 HaiFeiArrangeProject[28531:342082] 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 = 0;    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(2);        NSLog(@"1----------%@",[NSThread currentThread]);    });        dispatch_group_async(group, self.concurrentQueue, ^{        sleep(1);        NSLog(@"2----------%@",[NSThread currentThread]);    });        dispatch_group_async(group, self.concurrentQueue, ^{        sleep(3);        NSLog(@"3----------%@",[NSThread currentThread]);    });        //4.拦截通知    dispatch_group_notify(group, self.concurrentQueue, ^{        NSLog(@"---dispatch_group_notify------%@",[NSThread currentThread]);    });    //不用等待 队列执行完就会执行这个代码    NSLog(@"----group--end----");
View Code

这个代码是 加入到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(3);            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(2);            NSLog(@"2--2-end");            dispatch_group_leave(group);                    });    });        dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{        NSLog(@"%@---全部done。。。",[NSThread currentThread]);    });        NSLog(@"main");
enter 和 leave
2019-01-26 16:32:11.860953+0800 HaiFeiArrangeProject[30753:447579] 1---1--begin     2019-01-26 16:32:11.860953+0800 HaiFeiArrangeProject[30753:447327] main     2019-01-26 16:32:11.860957+0800 HaiFeiArrangeProject[30753:447367] 2---2--begin     2019-01-26 16:32:13.861316+0800 HaiFeiArrangeProject[30753:447367] 2--2-end     2019-01-26 16:32:14.866069+0800 HaiFeiArrangeProject[30753:447579] 1---1--end     2019-01-26 16:32:14.866708+0800 HaiFeiArrangeProject[30753:447579] 
{number = 3, name = (null)}---全部done。。。
使用后enter和leave的打印输出
2019-01-26 16:33:19.111523+0800 HaiFeiArrangeProject[30784:448544] 1---1--begin     2019-01-26 16:33:19.111520+0800 HaiFeiArrangeProject[30784:448504] main     2019-01-26 16:33:19.111544+0800 HaiFeiArrangeProject[30784:448871] 2---2--begin     2019-01-26 16:33:19.111605+0800 HaiFeiArrangeProject[30784:448868] 
{number = 3, name = (null)}---全部done。。。 2019-01-26 16:33:21.113975+0800 HaiFeiArrangeProject[30784:448871] 2--2-end 2019-01-26 16:33:22.114889+0800 HaiFeiArrangeProject[30784:448544] 1---1--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(2);        NSLog(@"1");    });    dispatch_group_async(group, self.concurrentQueue, ^{        sleep(1.5);        NSLog(@"2");    });    dispatch_group_async(group, self.concurrentQueue, ^{        sleep(3);        NSLog(@"3");    });    NSLog(@"aaaaa");    dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);    long result = dispatch_group_wait(group, time);    if (result == 0){        // 属于Dispatch Group的Block全部处理结束        NSLog(@"全部处理结束");    }else{        // 属于Dispatch Group的某一个处理还在执行中        NSLog(@"某一个处理还在执行中");    }    NSLog(@"main");
wait代码示例
2019-01-26 15:56:15.450252+0800 HaiFeiArrangeProject[30168:420683] aaaaa     2019-01-26 15:56:16.453528+0800 HaiFeiArrangeProject[30168:420709] 2     2019-01-26 15:56:17.451638+0800 HaiFeiArrangeProject[30168:420683] 某一个处理还在执行中     2019-01-26 15:56:17.451927+0800 HaiFeiArrangeProject[30168:420683] main     2019-01-26 15:56:17.453986+0800 HaiFeiArrangeProject[30168:420707] 1     2019-01-26 15:56:18.453192+0800 HaiFeiArrangeProject[30168:420706] 3
wait2s的打印输出
2019-01-26 15:57:15.072096+0800 HaiFeiArrangeProject[30189:421617] aaaaa     2019-01-26 15:57:16.075428+0800 HaiFeiArrangeProject[30189:421665] 2     2019-01-26 15:57:17.076848+0800 HaiFeiArrangeProject[30189:421915] 1     2019-01-26 15:57:18.072394+0800 HaiFeiArrangeProject[30189:421920] 3     2019-01-26 15:57:18.072845+0800 HaiFeiArrangeProject[30189:421617] 全部处理结束     2019-01-26 15:57:18.073139+0800 HaiFeiArrangeProject[30189:421617] 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(1);for (int i = 0; i < 10000; 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(1);    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 = 0; i < 10; i++) {        dispatch_group_async(group, self.concurrentQueue, ^{                        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);            NSLog(@"%d --- 开始 --",i + 1 );            // 线程操作区域 最多有两个线程在此做事情            sleep(2);                       NSLog(@"%d --- end --",i + 1 );            dispatch_semaphore_signal(semaphore);        });    }    // group任务全部执行完毕回调    dispatch_group_notify(group, self.concurrentQueue, ^{        NSLog(@"done");    });
控制并发个数

备注:在应用场景上,限制线程并发数是为了性能考虑,而加锁是为了安全而考虑。

遗留问题:信号量是否线程安全?

文中若有不对之处,还请劳驾之处,谢谢!

 

信号量部分分析参考自:https://www.jianshu.com/p/de75da4173cf !

 

转载于:https://www.cnblogs.com/lisaloveyou1900/p/10324542.html

你可能感兴趣的文章
常用算法计算方式
查看>>
【LAMP】在Debian系linux下安装LAMP
查看>>
Xamarin.Android多界面
查看>>
[MySQL 5.6] MySQL 5.6 group commit 性能测试及内部实现流程
查看>>
[Android Pro] Android应用性能测试之CPU和内存占用(转载)
查看>>
Linux Shell 小脚本经典收藏
查看>>
Log4Net的使用
查看>>
金刚经的40句名言
查看>>
【BZOJ】1084: [SCOI2005]最大子矩阵(DP)
查看>>
一套后台管理html模版
查看>>
安卓第八夜 玛丽莲梦露
查看>>
Sublime 脚本 配置 (lua 和 JavaScript篇)
查看>>
mysql大小写敏感与校对规则
查看>>
MongoDb Windows linux平台环境及主流编程语言驱动安装同时配置mongoDb的远程连接
查看>>
PLSQL_数据泵导入导出数据Impdp/ Expdp(概念)
查看>>
Smarty3学习笔记
查看>>
[翻译] BTSimpleRippleButton
查看>>
JSP中Session的使用
查看>>
TFS(Team Foundation Server)介绍和入门
查看>>
启动TDS LDAP 服务器遇到的问题总结
查看>>