GCD
来源:互联网 发布:js设置div的margin 编辑:程序博客网 时间:2024/06/15 15:54
GCD 常用的几种语句:
dispatch_once_t必须是全局或static变量
dispatch_queue_create,创建队列用的
dispatch_after是延迟提交,不是延迟运行
dispatch_time_t
dispatch_suspend != 立即停止队列的运行
“同步”的dispatch_apply
dispatch_group
1、dispatch_once_t的使用。注意下 非全局或非static的dispatch_once_t变量在使用时会导致非常不好排查的bug
/**
* 单例
*/
//声明静态变量或者全局变量
staticdispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//实例对象的代码
});
//创建串行队列
dispatch_queue_t serialqueue =dispatch_queue_create("serialQueue",DISPATCH_QUEUE_SERIAL);
//创建并发队列
dispatch_queue_t concurrentqueue =dispatch_queue_create("concurrentQueue",DISPATCH_QUEUE_CONCURRENT);
//创建串行队列
dispatch_queue_t serialqueue =dispatch_queue_create("serialQueue",DISPATCH_QUEUE_SERIAL);
//异步线程
dispatch_async(serialqueue, ^{
//在线程中的操作如加载图片
});
//创建并发队列
dispatch_queue_t concurrentqueue =dispatch_queue_create("concurrentQueue",DISPATCH_QUEUE_CONCURRENT);
//同步线程
dispatch_sync(concurrentqueue, ^{
//在线程中的操作如加载图片
});
3、dispatch_after是延迟提交,不是延迟运行。官方文档 指的就是将一个Block在特定的延时以后,加入到指定的队列中,不是在特定的时间后立即运行!
举个例子
//创建并发队列
dispatch_queue_t concurrentqueue =dispatch_queue_create("concurrentQueue",DISPATCH_QUEUE_CONCURRENT);
NSLog(@"我开始进入线程");
//同步线程
dispatch_async(concurrentqueue, ^{
//在线程中的操作如加载图片
NSLog(@"我在线程中,将要进行睡眠5秒");
//休眠语句
[NSThreadsleepForTimeInterval:5];
NSLog(@"我在线程中,已经睡眠5秒");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)1*NSEC_PER_SEC), concurrentqueue, ^{
NSLog(@"1秒我将after");
});
2017-10-23 17:00:15.363 NewGCD[805:43858] 我开始进入线程
2017-10-23 17:00:15.364 NewGCD[805:43904] 我在线程中,将要进行睡眠5秒
2017-10-23 17:00:16.456 NewGCD[805:43921] 1秒我将after
2017-10-23 17:00:20.369 NewGCD[805:43904] 我在线程中,已经睡眠5秒
//创建并发队列
dispatch_queue_t concurrentqueue =dispatch_queue_create("concurrentQueue",DISPATCH_QUEUE_CONCURRENT);
NSLog(@"我开始进入线程");
//同步线程
dispatch_async(concurrentqueue, ^{
//在线程中的操作如加载图片
NSLog(@"我在线程中,将要进行睡眠5秒");
//休眠语句
//[NSThread sleepForTimeInterval:5];❤️
NSLog(@"我在线程中,已经睡眠5秒");
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)1*NSEC_PER_SEC), concurrentqueue, ^{
NSLog(@"1秒我将after");
});
2017-10-23 17:13:42.448 NewGCD[825:46635] 我开始进入线程
2017-10-23 17:13:42.448 NewGCD[825:46684] 我在线程中,将要进行睡眠5秒
2017-10-23 17:13:42.449 NewGCD[825:46684] 我在线程中,已经睡眠5秒
2017-10-23 17:13:43.547 NewGCD[825:46667] 1秒我将after
dispatch_time_t 和after一起连用
dispatch_time_t time1 = dispatch_time ( dispatch_time_t when, int64_t delta );
从什么时间开始DISPATCH_TIME_NOW 时间间隔
NSEC:纳秒。
USEC:微妙。
SEC:秒
PER:每
#define NSEC_PER_SEC 1000000000ull 9 0的个数
#define USEC_PER_SEC 1000000ull 6
#define NSEC_PER_USEC 1000ull 3
NSEC_PER_SEC,每秒有多少纳秒。 秒
USEC_PER_SEC,每秒有多少毫秒。(注意是指在纳秒的基础上) 毫秒
NSEC_PER_USEC,每毫秒有多少纳秒。
dispatch_queue_t concurrentqueue =dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentqueue, ^{
[NSThreadsleepForTimeInterval:5];
NSLog(@"延时5s后打印");
});
dispatch_async(concurrentqueue, ^{
[NSThreadsleepForTimeInterval:5];
NSLog(@"在上一个延时基础上,在延时5s");
});
NSLog(@"准备接下来1s休眠");
[NSThreadsleepForTimeInterval:1];
//
NSLog(@"开始挂起concurrentqueue");
dispatch_suspend(concurrentqueue);
NSLog(@"开始延迟10s");
[NSThreadsleepForTimeInterval:10];
NSLog(@"开始恢复concurrentqueue");
dispatch_resume(concurrentqueue);
注意观察时间 和你用的什么队列2017-10-23 17:41:33.362 NewGCD[918:58023] 准备接下来1s休眠
2017-10-23 17:41:34.363 NewGCD[918:58023] 开始挂起concurrentqueue
2017-10-23 17:41:34.364 NewGCD[918:58023] 开始延迟10s
2017-10-23 17:41:38.367 NewGCD[918:58062] 延时5s后打印
2017-10-23 17:41:38.367 NewGCD[918:58079] 在上一个延时基础上,在延时5s
2017-10-23 17:41:44.365 NewGCD[918:58023] 开始恢复concurrentqueue
从第一个nslog的时间和第一个线程休眠5s的时间之差是多少秒吗。 5s说明什么 说明你都dispatch_suspend但不影响dispatch_async。我这用的并发队列不是很明显,你们自己改成串行队列 就可以很清楚的看见。
总体想说的是dispatch_suspend 不是停止正在进行的线程,要等正在执行的执行完后停止,等着恢复dispatch_resume
5、dispatch_apply
dispatch_apply的作用是在一个队列(串行或并行)上“运行”多次block,其实就是简化了用循环去向队列依次添加block任务。起个循环的作用
dispatch_queue_t serialqueue =dispatch_queue_create("serialQueue",DISPATCH_QUEUE_SERIAL);
dispatch_apply(4, serialqueue, ^(size_t i) {
NSLog(@"执行%ld次",i);
});
NSLog(@"执行完");
2017-10-23 17:58:18.898 NewGCD[944:62741] 执行0次
2017-10-23 17:58:18.899 NewGCD[944:62741] 执行1次
2017-10-23 17:58:18.899 NewGCD[944:62741] 执行2次
2017-10-23 17:58:18.899 NewGCD[944:62741] 执行3次
2017-10-23 17:58:18.899 NewGCD[944:62741] 执行完
都是同时进行的从时间上看。dispatch_apply将外面的线程(main线程)“阻塞”dispatch_sync导致的死锁
//在main线程使用“同步”方法提交Block,必定会死锁。
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@
"I am block..."
);
});
- (void)updateUI1 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@
"Update ui 1"
);
//死锁!
[self updateUI2];
});
}
- (void)updateUI2 {
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@
"Update ui 2"
);
});
}
在你不注意的时候,嵌套调用可能就会造成死锁
dispatch_queue_t queue = dispatch_queue_create(
"me.tutuge.test.gcd"
, DISPATCH_QUEUE_SERIAL);
dispatch_apply(3, queue, ^(size_t i) {
NSLog(@
"apply loop: %zu"
, i);
//再来一个dispatch_apply!死锁!
dispatch_apply(3, queue, ^(size_t j) {
NSLog(@
"apply loop inside %zu"
, j);
});
});
6、dispatch_group
如果是有序的任务,可以分步骤完成的,直接使用串行队列就行。但是如果是一系列并行执行的任务呢?这个时候,就需要dispatch_group帮忙了~(收尾工作)
dispatch_group的使用分如下几步:
创建dispatch_group_t
添加任务(block)
添加结束任务(如清理操作、通知UI等)
dispatch_group_t group =dispatch_group_create();
dispatch_queue_t queue =dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
//do some
});
af需要联合dispatch_group_enter,dispatch_group_leaveAFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
//Enter group
dispatch_group_enter(group);
[manager GET:@
"http://www.baidu.com"
parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Deal with result...
//Leave group
dispatch_group_leave(group);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Deal with error...
//Leave group
dispatch_group_leave(group);
}];
添加结束任务也可以分为两种情况,如下:
在当前线程阻塞的同步等待:dispatch_group_wait。
添加一个异步执行的任务作为结束任务:dispatch_group_notify
dispatch_group_t group =dispatch_group_create();
dispatch_queue_t queue =dispatch_queue_create("concurrent",DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
//do some
});
//结束group
dispatch_group_wait(group,DISPATCH_TIME_NOW);
dispatch_queue_t queue2 =dispatch_queue_create("DISPATCH_QUEUE_CONCURRENT",DISPATCH_QUEUE_CONCURRENT);
dispatch_group_notify(group, queue2, ^{
});
dispatch_barrier_async 简单来说就是插入一个线程,如果你插入的时候有线程在运行,那就等线程执行完后在执行dispatch_barrier_async 在执行插入处下一个任务
值得注意的是:
dispatchbarrier\(a)sync只在自己创建的并发队列上有效,在全局(Global)并发队列、串行队列上,效果跟dispatch_(a)sync效果一样。
dispatch_set_context与dispatch_set_finalizer_f的配合使用
dispatch_set_context可以为队列添加上下文数据,但是因为GCD是C语言接口形式的,所以其context参数类型是“void *”。也就是说,我们创建context时有如下几种选择:
用C语言的malloc创建context数据。
用C++的new创建类对象。
用Objective-C的对象,但是要用__bridge等关键字转为Core Foundation对象。
以上所有创建context的方法都有一个必须的要求,就是都要释放内存!,无论是用free、delete还是CF的CFRelease,我们都要确保在队列不用的时候,释放context的内存,否则就会造成内存泄露。
所以,使用dispatch_set_context的时候,最好结合dispatch_set_finalizer_f使用,为队列设置“析构函数”,在这个函数里面释放内存,大致如下:
void cleanStaff(void *context) {
//释放context的内存!
//CFRelease(context);
//free(context);
//delete context;
}
...
//在队列创建后,设置其“析构函数”
dispatch_set_finalizer_f(queue, cleanStaff);
就是在清除context的函数里面,用“_bridge_transfer”转换context,把context的内存管理权限重新交给ARC,这样,就不用显式调用“CFRelease”了
void cleanStaff(void *context) {//这里用_bridge_transfer转换,将内存管理权限交还给ARCData *data = (_bridge_transfer Data *)(context);NSLog(@"In clean, context number: %d", data.number);//不用显式释放context的内存!}
我们都知道,GCD的接口参数都是“C语言类型“的,那么,我们如何将NSObject类型(Foundation框架)的数据,传入GCD的接口呢?即Core Foundation和Foundation对象的转换
两个函数分别完成了将context“绑定”到特定GCD队列和从GCD队列获取对应context的任务。//设置contextvoid dispatch_set_context (dispatch_object_t object, void *context);//获取contextvoid* dispatch_get_context (dispatch_object_t object);
dispatch_object_t object通过dispatch_queue_create创建的队列
//定义context,即一个结构体typedef struct _Data {int number;} Data;//定义队列的finalizer函数,用于释放context内存void cleanStaff(void *context) {NSLog(@"In clean, context number: %d", ((Data *)context)->number);//释放,如果是new出来的对象,就要用deletefree(context);}- (void)runTest {//创建队列dispatch_queue_t queue = dispatch_queue_create("me.tutuge.test.gcd", DISPATCH_QUEUE_SERIAL);//创建Data类型context数据并初始化Data *myData = malloc(sizeof(Data));myData->number = 10;//绑定contextdispatch_set_context(queue, myData);//设置finalizer函数,用于在队列执行完成后释放对应context内存dispatch_set_finalizer_f(queue, &cleanStaff);dispatch_async(queue, ^{//获取队列的context数据Data *data = dispatch_get_context(queue);//打印NSLog(@"1: context number: %d", data->number);//修改context保存的数据data->number = 20;});}
- 在dispatch_set_context的时候用__bridge_retained转换,将context的内存管理权从ARC移除,交给我们自己管理。
- 在队列任务中,用dispatch_get_context获取context的时候,用__bridge转换,维持context的内存管理权不变,防止出了作用域context被释放。
- 最后用CFRelease释放context内存。
- __bridge: 只做了类型转换,不修改内存管理权;
- __bridge_retained(即CFBridgingRetain)转换类型,同时将内存管理权从ARC中移除,后面需要使用CFRelease来释放对象;
- __bridge_transfer(即CFBridgingRelease)将Core Foundation的对象转换为Objective-C的对象,同时将内存管理权交给ARC。
- gcd
- GCD
- GCD
- GCD
- GCD
- GCD
- GCD
- GCD
- GCD
- GCD
- GCD
- GCD
- gcd
- GCD
- GCD
- GCD
- GCD
- GCD
- idea的Debug模式
- python 中文显示 \xb2\xe2\xca\xd4\xd6\xf7\xbb\xfa
- Java集合框架Collection/Map
- js入门笔记
- 一次给多张表添加相同的字段
- GCD
- ApkTool命令行工具
- Audio alaysis and Deep Learning
- C#编写C/S端代码,Windows应用程序 --- 初接触(一)
- 有趣的位运算
- oracle延时约束的问题
- 用Redis做单点登录(理论)
- stylus 为啥要加入 ~ @import '~common/stylus/mixin'
- php多站点配置以及Forbidden You don't have permission to access / on this server问题解决