美文网首页
2.多线程之GCD

2.多线程之GCD

作者: 王洛书 | 来源:发表于2016-08-06 14:49 被阅读0次

1.GCD - Grand Central Dispatch 纯C语言;苹果为多核处理器的并行运算提供的解决方案,自动调度cpu内核,自动管理线程生命周期(创建线程、调度任务、销毁线程)。

2.任务、队列

任务:要执行的操作(逻辑代码)

队列:存放任务。GCD会自动从队列中取出任务,在相应的线程中执行,遵循FIFO原则,先进先出,后进后出

3.执行任务:同步、异步

同步:不能开启新线程

异步:可以开启新线程,可以创建多条线程,并发执行任务

4.队列类型:并发、串行

并发(Concurrent Dispatch Queue):多个任务同时执行,同时开启多个线程,每个线程执行一个任务,但是只能在异步下有效

串行(Serial Dispatch Queue):多个任务排队按顺序执行

5.任务与队列的四种组合方式:同步并发、同步串行、异步并发、异步串行。

5.1 同步并发代码:

// 1.获得全局的并发队列,供整个应用使用,不用手动创建

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 2.将任务加入队列

dispatch_sync(queue, ^{

NSLog(@"1-----%@", [NSThread currentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"2-----%@", [NSThread currentThread]);

});

控制台输出:

2016-08-06 11:48:17.716 07-GCD的基本使用[63024:4677093] 1-----{number = 1, name = main}

2016-08-06 11:48:17.717 07-GCD的基本使用[63024:4677093] 2-----{number = 1, name = main}

--- 对全局并发队列参数做一解释:

第一个参数--队列优先级:默认使用第二个

#define DISPATCH_QUEUE_PRIORITY_HIGH 2

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0

#define DISPATCH_QUEUE_PRIORITY_LOW (-2)

#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

第二个参数--unsigned long flags,此参数作为以后的扩展参数使用,目前传0就可以

5.2 同步串行:不开启新线程,在当前线程执行任务,排队执行,一个接一个

// 1.创建串行队列

dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_SERIAL);

// 2.将任务加入队列

dispatch_sync(queue, ^{

NSLog(@"1-----%@", [NSThread currentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"2-----%@", [NSThread currentThread]);

});

控制台输出:

2016-08-06 14:22:40.753 07-GCD的基本使用[63168:4815767] 1-----{number = 1, name = main}

2016-08-06 14:22:40.753 07-GCD的基本使用[63168:4815767] 2-----{number = 1, name = main}

---对手动创建线程参数做出解释:

dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

const char *label -- 线程标识,随便写一个唯一的 - 例如:"etong.queue"

dispatch_queue_attr_t attr -- 队列属性

#define DISPATCH_QUEUE_SERIAL NULL

#define DISPATCH_QUEUE_CONCURRENT

所以创建队列也可以这么写:dispatch_queue_t queue = dispatch_queue_create("etong.queue", NULL);

5.3 异步并发:可同时开始多条线程,可以手动创建、也可以使用全局并发队列

// 1.创建一个并发队列

// label : 相当于队列的名字

//    dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_CONCURRENT);

// 1.获得全局的并发队列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 2.将任务加入队列

dispatch_async(queue, ^{

for (NSInteger i = 0; i<3; i++) {

NSLog(@"1-----%@", [NSThread currentThread]);

}

});

dispatch_async(queue, ^{

for (NSInteger i = 0; i<3; i++) {

NSLog(@"2-----%@", [NSThread currentThread]);

}

});

控制台输出:

2016-08-06 14:35:16.894 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}

2016-08-06 14:35:16.894 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}

2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}

2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}

2016-08-06 14:35:16.895 07-GCD的基本使用[63188:4876010] 1-----{number = 2, name = (null)}

2016-08-06 14:35:16.896 07-GCD的基本使用[63188:4876024] 2-----{number = 3, name = (null)}

要说明的是:异步并发函数块执行代码的顺序--是将函数块的代码执行完之后,才会返回去执行队列中任务。--重要

5.4 异步串行 会开启新的线程,任务还是排队依次执行

// 1.创建串行队列

dispatch_queue_t queue = dispatch_queue_create("etong.queue", DISPATCH_QUEUE_SERIAL);

//    dispatch_queue_t queue = dispatch_queue_create("etong.queue", NULL);

// 2.将任务加入队列

dispatch_async(queue, ^{

NSLog(@"1-----%@", [NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"2-----%@", [NSThread currentThread]);

});

控制台输出:

2016-08-06 14:40:40.694 07-GCD的基本使用[63202:4904803] 1-----{number = 2, name = (null)}

2016-08-06 14:40:40.694 07-GCD的基本使用[63202:4904803] 2-----{number = 2, name = (null)}

*还有2种主队列的情况

5.5 异步+主队列:只在主线程中执行任务

// 1.获得主队列

dispatch_queue_t queue = dispatch_get_main_queue();

// 2.将任务加入队列

dispatch_async(queue, ^{

NSLog(@"1-----%@", [NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"2-----%@", [NSThread currentThread]);

});

控制台输出:

2016-08-06 14:43:13.921 07-GCD的基本使用[63217:4920120] 1-----{number = 1, name = main}

2016-08-06 14:43:13.921 07-GCD的基本使用[63217:4920120] 2-----{number = 1, name = main}

5.6 同步+主队列:这个比较奇葩

NSLog(@"++++++++++++");

// 1.获得主队列

dispatch_queue_t queue = dispatch_get_main_queue();

// 2.将任务加入队列

dispatch_sync(queue, ^{

NSLog(@"1-----%@", [NSThread currentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"2-----%@", [NSThread currentThread]);

});

dispatch_sync(queue, ^{

NSLog(@"3-----%@", [NSThread currentThread]);

});

NSLog(@"-------------");

控制台输出:

2016-08-06 14:44:50.115 07-GCD的基本使用[63229:4929440] ++++++++++++

-- 对同步+主队列输出解释一下:因为是同步,所以等待任务依次执行,但对于开启的多个任务来说,需要等待函数块执行完,执行队列中的任务,这样双方都在等待,导致程序卡死不动。

6.线程间通信:基本上是子线程下载的数据需要在主线程完成加载刷新

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

// 图片的网络路径

NSURL *url = [NSURL URLWithString:@"https://ss0.baidu.com/6ONWsjip0QIZ8tyhnq/it/u=1537639143,1137876288&fm=58"];

// 加载图片

NSData *data = [NSData dataWithContentsOfURL:url];

// 生成图片

UIImage *image = [UIImage imageWithData:data];

// 回到主线程

dispatch_async(dispatch_get_main_queue(), ^{

self.imageView.image = image;

});

});

7.其他GCD函数的使用

7.1 延时执行:有三种延时执行的方法

7.1.1 调用NSObject方法

   [self performSelector:@selector(run) withObject:nil afterDelay:2.0];

7.1.2 使用GCD函数

   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        NSLog(@"run-----");

    });

解释一下:2.0是需要延时的秒数;dispatch_get_main_queue()根据需要创建队列

7.1.3 调用NSTimer方法

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];

7.2 一次性GCD函数:保证block中代码在程序运行期间只被执行一次

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

NSLog(@"------run");//只执行一次,默认是线程安全的

});

7.3 拦截函数

dispatch_queue_t queue = dispatch_queue_create("123456", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

NSLog(@"----1-----%@", [NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----2-----%@", [NSThread currentThread]);

});

dispatch_barrier_async(queue, ^{

NSLog(@"----barrier-----%@", [NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----3-----%@", [NSThread currentThread]);

});

dispatch_async(queue, ^{

NSLog(@"----4-----%@", [NSThread currentThread]);

});

控制台输出:

2016-08-06 15:06:40.230 09-掌握-GCD的其他常用函数[63329:5037819] ----1-----{number = 2, name = (null)}

2016-08-06 15:06:40.230 09-掌握-GCD的其他常用函数[63329:5037834] ----2-----{number = 3, name = (null)}

2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037834] ----barrier-----{number = 3, name = (null)}

2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037834] ----3-----{number = 3, name = (null)}

2016-08-06 15:06:40.231 09-掌握-GCD的其他常用函数[63329:5037819] ----4-----{number = 2, name = (null)}

--解释一下:barrier-栅栏,起拦截作用,他前面的任务执行完毕才能继续往下执行。

此方法创建的队列只能是手动创建,不能使用全局并发队列,否则不起作用

7.4 队列组:需求--分别异步执行耗时操作,完毕后,回到主线程操作

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 创建一个队列组

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{

//执行一个耗时操作

});

dispatch_group_async(group, queue, ^{

//执行一个耗时操作

});

dispatch_group_notify(group, queue, ^{

dispatch_async(dispatch_get_main_queue(), ^{

//回到主线程

});

});

7.5 快速迭代函数:此方法可以在多条线程下同时完成任务

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(subpaths.count, queue, ^(size_t index) {

});

-- 对快速迭代函数做出解释:for循环遍历10个变量的话,是顺序挨个遍历;而GCD快速迭代是同时开始10个线程一次遍历完,几乎是同时的。



相关文章

网友评论

      本文标题:2.多线程之GCD

      本文链接:https://www.haomeiwen.com/subject/aiyksttx.html