GCD的简单使用
2017-10-22 本文已影响15人
3fbfd3c15df9
GCD简单使用
- GC使用就2个步骤
- 定制任务
- 将任务添加到队列中,gcd会自动将队列中的任务取出,放到对应的线程中执行,任务遵循队列:先进先出,后进后出 ,2个口 。。。。栈内存:先进后出,后进先出,一个口
gcd2个用来执行任务的常用函数
//异步 queue 队列 block 任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
//同步
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
//这一句代码做了2件事 ,1,封装任务 ,2,把任务丢到队列中
gcd 队列分2大类。
队列的作用 1:队列就是用来装任务的,并且安排队列的任务到那个线程中执行 , 封装任务 , 2:让任务到线程中执行
- 并发队列 2.串行队列
并发队列
1. 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
2. 并发功能只能在异步函数先才有效(dispatch_async)
串行队列
1.任务一个接一个的执行
同步异步 的区别
- 同步:只能在当前线程中执行任务,不具备开新线程的能力
- 异步 :可以在新的线程中执行任务,具备开启新线程的能力
术语之间的区别于影响,同步,异步,并发,串行
同步函数(dispatch_sync)异步函数(dispatch_async)主要影响能否开启新的线程 , 同步函数,立刻马上执行。他不执行完就不能进行下一条
同步函数:只能在当前线程中执行任务,执行完成后进行下一条任务,不具备开新线程的能力
异步函数 :可以在新的线程中执行任务,具备开启新线程的能力
串行队列和并发队列主要影响任务的执行方式
并发队列:允许多个任务并发执行
串行串行:一个任务完成才能执行下一个任务
一些组合例子
创建 异步函数 + 并发队列
// 创建 异步函数 + 并发队列:次方法可以开多条线程,队列任务是异步(即并发)执行的
-(void)asyncConcurrent
{
/*
一 ,创建队列
一个队列可以添加多个任务
1,参数一:C语言字符串,随便给他命名
2,参数二:队列的类型 DISPATCH_QUEUE_CONCURRENT 并发队列
DISPATCH_QUEUE_SERIAL 串行队列
*/
dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_CONCURRENT);
//二,封装任务
/*
参数1: 队列
参数2: 要执行的任务
*/
dispatch_async(queue, ^{
NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
});
}
异步函数 + 串行队列
// 异步函数 + 串行队列 ;是可以开线程的,只开一条线程,是串行执行的
-(void)asyncSerial
{
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_SERIAL);
//异步函数
dispatch_async(queue, ^{
NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
});
}
同步函数 + 并发队列
//同步函数 + 并发队列 ;不会开线程的,因为是同步函数,
//同步函数不具备开线程的能力,所以任务是串行执行的
-(void)syncConcurrent
{
//创建并发队列
dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_CONCURRENT);
//同步函数
dispatch_sync(queue, ^{
NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
});
}
同步函数 + 串行队列
//同步函数 + 串行队列 ;
//不会开线程的,因为是同步函数,同步函数不具备开线程的能力,所以任务是串行执行的
-(void)syncSerial
{
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("xc.download", DISPATCH_QUEUE_SERIAL);
//异步函数
dispatch_sync(queue, ^{
NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
});
}
只有:异步函数 + 并发队列:次方法可以开多条线程,队列任务是异步(即并发)执行的,这样才是并发执行。其余几种组合都是串行执行
gcd里面开多少条线程不是我们决定的,由系统决定
全局并发队列
//获得全局队列
/*
1,第一个参数:优先级
系统原来就带有的 ,
2,第二个参数暂时没什么用,传0 即可
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
GCD主队列
1. 主队列是GCD自带的一种特殊的串行队列
2.只要在主队列任务,都会放到主线程中去执行
3.使用 dispatch_get_main_queue()获得主队列
队列特点是
- 封装任务, 2. 安排任务在哪个线程中去执行。
主队列特点
主队列,会把他放到主线程中去执行,如果主队列有任务在执行,
那么主队列会暂停调用队列中的任务,知道主线程空闲为止
异步函数 + 主队列
//异步函数 + 主队列 。 不会开线程,所有任务主线程中去执行
-(void)asyncMain
{
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue() ;
//异步函数
dispatch_async(queue, ^{
NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
});
}
注意:同步函数 + 主队列 ,如果 在主线程中调用会报 错误 。 只能开启一条子线程,在 调用 同步函数 + 主队列 就可以了
//同步函数 + 主队列 。 错误。会造成死锁,。 因为主队列只能在主线程中执行,,
//如果 主线程 调用syncMain,方法会死锁,
因为调用方法发现 dispatch_sync 是同步函数,同步函数的任务放到
主队列,然后因为是主队列,会把他放到主线程中去执行,而如果主线程调
用了syncMain方法,里面是同步函数,同步函数在等主线程任务执行
结束,而主线程在等同步函数执行完毕,这样就造成了死锁。所以要用
子线程去调用
*/
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//开启子线程调用
[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];
}
-(void)syncMain
{
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue() ;
//同步函数
dispatch_sync(queue, ^{
NSLog(@"NSThread1 = , %@" ,[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"NSThread2 = , %@" ,[NSThread currentThread]);
});
}
总结下:只有在并发队列和异步函数,才会开启新线程。
1,并发队列+异步函数 = 有开启新线程,并发执行任务
2,串行队列+异步函数 = 有开一天新线程,串行执行
3,其余情况都是,不会开启新线程,串行执行 (例:并发队列,同步函数 or串行队列 + 异步函数 等)
GCD 线程间的通信
//GCD间的线程通信
//创建子线程下载图片
//获得全局主队列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//子线程下载图片
NSString *str = @"http://static.firefoxchina.cn/img/201710/4_59e999c8e6aa50.jpg";
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:str]];
UIImage *image = [UIImage imageWithData:data];
NSLog(@"%@" ,[NSThread currentThread]);
//回到主线程更新UI ,注意 同步函数 和 主队列。死锁情况哟
dispatch_async(dispatch_get_main_queue(), ^{
self.imageview.image = image ;
NSLog(@"%@" ,[NSThread currentThread]);
});
});
GCD一些常用方法
- 延迟调用方法
/**
方法一:延时调用改方法
参数1: 调用的方法
参数2:方法的参数
参数3:延时的时间
*/
[self performSelector:@selector(run:) withObject:nil afterDelay:2.0];
/**
方法二
延时2s调用 run方法
@param run: 方法
*/
[NSTimer scheduledTimerWithTimeInterval:2.0f target:self selector:@selector(run:) userInfo:nil repeats:YES];
/**
方法三
延时2s
DISPATCH_TIME_FOREVER
@param DISPATCH_TIME_NOW ->从现在开始
@param int64_t 延迟的时间
@return
dispatch_get_main_queue 我们可以改变队列,来改变延时后在那个线程中执行
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
栅栏函数
并发队列多个任务,那么我们想要某个任务,最后执行怎么办 ,所以用栅栏函数
注意:栅栏函数不能使用 全局并发队列,不然栅栏函数没有效果
//栅栏函数
-(void)barriermethod
{
//创建并发队列 。栅栏函数注意不能用全局并发队列
dispatch_queue_t queue = dispatch_queue_create("xc_queue", DISPATCH_QUEUE_CONCURRENT);
//异步函数 。 3个人任务并发执行,无顺序 。 那么怎么给他们安排顺序呢。 ----->栅栏函数。
dispatch_async(queue, ^{
for (int i = 0; i< 100; i++) {
NSLog(@"downLoad1 = %@" ,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i< 100; i++) {
NSLog(@"downLoad2 = %@" ,[NSThread currentThread]);
}
});
//我们需要任务3最后执行。所以我们用栅栏函数
//栅栏
dispatch_barrier_async(queue, ^{
NSLog(@"x-x-xx");
});
dispatch_async(queue, ^{
NSLog(@"downLoad3 = %@" ,[NSThread currentThread]);
});
}
GCD的快速迭代 ,迭代就是遍历的意思
for 循环就是迭代。 for循环是同步的
//dispatch快速迭代
-(void)apply
{
/**
快速迭代, dispatch_apply 会开启子线程去遍历。
@param iterations#> 迭代的次数 description#>
@param queue#> 队列。只能传并发队列,传串行队列就和for一样了没有意义,不能传主队列,可以全局并发队列 description#>
@param size_t index 索引
*/
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
NSLog(@"%zd --%@",index,[NSThread currentThread]);
});
}
/* 打印结果。有多个线程
2017-10-23 23:33:19.605 NSTread基本使用[1293:46453] 2 --<NSThread: 0x6000002603c0>{number = 4, name = (null)}
2017-10-23 23:33:19.605 NSTread基本使用[1293:46418] 0 --<NSThread: 0x608000079c40>{number = 1, name = main}
2017-10-23 23:33:19.606 NSTread基本使用[1293:46455] 3 --<NSThread: 0x6080002676c0>{number = 5, name = (null)}
2017-10-23 23:33:19.606 NSTread基本使用[1293:46453] 4 --<NSThread: 0x6000002603c0>{number = 4, name = (null)}
2017-10-23 23:33:19.606 NSTread基本使用[1293:46418] 5 --<NSThread: 0x608000079c40>{number = 1, name = main}
2017-10-23 23:33:19.606 NSTread基本使用[1293:46455] 6 --<NSThread: 0x6080002676c0>{number = 5, name = (null)}
2017-10-23 23:33:19.606 NSTread基本使用[1293:46453] 7 --<NSThread: 0x6000002603c0>{number = 4, name = (null)}
2017-10-23 23:33:19.606 NSTread基本使用[1293:46418] 8 --<NSThread: 0x608000079c40>{number = 1, name = main}
2017-10-23 23:33:19.606 NSTread基本使用[1293:46455] 9 --<NSThread: 0x6080002676c0>{number = 5, name = (null)}
2017-10-23 23:33:19.605 NSTread基本使用[1293:46452] 1 --<NSThread: 0x600000260380>{number = 3, name = (null)}
*/
队列组。
队列组里面的任务的进行情况。我们可以知道。是否完成,如果完成可以拿到这个完成事件
//队列组 ,简写步骤 1
-(void)groupMethod
{
//1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2.创建队列组
dispatch_group_t group = dispatch_group_create() ;
//3.异步函数创建,并队列组管理
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 100; i++) {
NSLog(@"downLoad1 = %@ , i = %d" ,[NSThread currentThread],i);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 100; i++) {
NSLog(@"downLoad2 = %@ , i = %d" ,[NSThread currentThread],i);
}
});
//离开通知,监听到
dispatch_group_notify(group, queue, ^{
NSLog(@"队列组任务完成。离开,在这里说明上面任务都完成了");
});
//和上面dispatch_group_notify 效果一样,特点队列组的任务完成后他才会执行
//dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
}
比较老一点的队列组写法
//队列组 第二种
-(void)groupMethod2
{
//1.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//2.创建队列组
dispatch_group_t group = dispatch_group_create() ;
//3.在该方法后面的异步任务会被纳入到队列的监听范围
dispatch_group_enter(group);
//4.异步函数创建,并队列组管理
dispatch_async(queue, ^{
for (int i = 0; i< 100; i++) {
NSLog(@"downLoad1 = %@" ,[NSThread currentThread]);
}
//5. 告诉队列组任务执行完毕了,离开群组 dispatch_group_leave(group);
dispatch_group_leave(group);
});
//dispatch_group_enter(group); dispatch_group_leave(group);配对的,必须以前出现
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (int i = 0; i< 100; i++) {
NSLog(@"downLoad2 = %@" ,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
//离开通知,监听到
dispatch_group_notify(group, queue, ^{
NSLog(@"队列组任务完成。离开,这个 内部是异步的");
});
}