iOS多线程--下(GCD)

来源:互联网 发布:淘宝买轮毂 编辑:程序博客网 时间:2024/06/03 04:29

1 GCD

它是一种纯C语言,它是为多核并列运算设计的。可以自动管理线程的生命周期。
GCD 是面向任务和队列的,不是面向线程的。他有两个关键字“任务”“队列”。
使用 GCD 的步骤主要是:
1 定制任务
2 任务添加到队列中,队列支持 FIFO 原则

#基本形式如下
dispath_queue_t queue = dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFULT,0);dispath_async(queue,^{    //任务代码段});

1.1 任务

1.1.1 同步方式

在当前线程中执行,不具备开启线程的能力。

dispath_sync( queue,block); //在队列中执行 block 所定义的任务,以同步的方式

1.1.2 异步方式

在新线程中执行,具备开启新线程的能力

dispath_async(queue,block); //在队列中执行 block 所定义的任务,以异步的方式

决定了是否有能力开启新的线程

1.2 队列

1.2.1 并行队列

并行队列,队列中的任务,以并行的方式进行,多任务同时进行。
一般情况下,我们使用的是全局的并发队列。获取全局并发队列的方式如下:

//默认的写法,获取全局并发队列。前一个参数是优先级默认,后面一个参数是苹果保留的参数dispath_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

1.2.2 串行队列

串行队列中,队列中的人物,都是以串行的方式进行。先执行一个任务,再执行另一个任务。
串行队列分为两种,一种是手动创建的串行队列,另一个是主队列

1.2.2.1 手动创建队列
//默认写法,第一个参数是队列名称,随便填写;第二个参数是队列属性,一般情况下写 NULLdispatch_queue_create("queueName",NULL);
1.2.2.2 主队列
//默认写法dispath_get_main_queue();主队列的任务执行,都是在主线程中进行的,一般用来做线程间的通信。

2 常见的组合方式

2.1 异步方式+并发队列(最常用)

①会创建新的线程
②并发执行任务
下面的例子中,任务1和任务2是并发执行的

//实例代码//①获取全局并发队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//②异步方式处理任务1dispatch_async(queue,^{    NSLog("下载图片1--%@",[NSThread currentThread]);});//②异步方式处理任务2dispatch_async(queue,^{    NSLog("下载图片2--%@",[NSThread currentThread]);});

2.2 异步方式+串行队列

①会创建新线程,因为是串行方式执行,一般情况下只会新建一条线程。
②队列中的任务以串行方式执行:任务1和任务2是串行执行的

//示例代码//①手动创建串行队列dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);//②异步方式处理任务1dispatch_async(queue,^{    NSLog("正在下载图片1---%@",[NSThread currentThread]);});//②异步方式处理任务2dispatch_async(queue,^{    NSLog("正在下载图片2---%@",[NSThread currentThread]);});

2.3 同步方式+并发队列

①不会创建线程
②并发队列的并发功能消失,队列中的所有任务串行执行。

//示例代码//①获取全局并发队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//②同步执行任务1dispatch_sync(queue,^{    NSLog(@"下载图片1---%@",[NSThread currentThread]);});//②同步执行任务2dispatch_sync(queue,^{    NSLog(@"下载图片2---%@",[NSThread currentThread]);});

2.4 同步方式+串行队列

①不会创建新线程
②串行队列中的所有任务串行执行

//示例代码//①手动创建串行队列dispatch_queue_t queue = dispatch_queue_create("queueName",NULL);//②同步执行任务1dispatch_sync(queue,^{    NSLog(@"正在下载图片1-----%@",[]NSThread currentThread]);});//②同步执行任务2dispatch_sync(queue,^{    NSLog(@"正在下载图片2-----%@",[]NSThread currentThread]);});

2.5 主队列+异步方式(主队列也是串行队列)

①主队列是特殊的队列,此时异步方式虽然具备创建新线程的能力,但是实际上并不能创建新线程
②所有的任务都是在主队列中串行执行的,也就是在主线程中进行,一般用来做进程之间的通信。

//①获取当前的主队列dispatch_queue_t queue = dispatch_get_main_queue();//②异步方式执行dispatch_async(queue,^{    NSLog(@"正在下载图片---%@",[NSThread currentThread]);});

2.6 主队列+同步方式(主队列也是串行队列)

  • 这种方式,会卡死整个程序,不用
  • 当前执行下面的程序是在主线程中执行的,当执行到第二步的时候,需要将下面的任务加入到主队列中串行执行。因为是串行执行,主线程必须等到第二步结束后才能继续向下执行,但是第二步是将任务加载到了主队列的末尾,必须要等到之前主线程队列执行完毕后才能执行,所以陷入了卡死状态。

    //1获取主队列dispatch_queue_t queue = dispatch_get_main_queue();//2同步执行主队列dispatch_sync(queue,^{    NSLog(@"正在执行。。。");});

3 队列的内存管理

需要遵循的原则:
* 凡是函数名中带有 create\copy\new\retain 等字眼,都应该在不需要使用这个数据的时候release 操作
* GCD的数据类型,在ARC下不需要再 release
* CF(Core Foundation)的数据类型在 ARC 和 MRC 环境下都需要手动 release。
例子:
* CFRelease(id);

NSDictionary *dict = @{@"1":@"1"};CFDictionaryRef dictCF = (__bridge CFDictionaryRef)(dict);CFRelease(dictCF);

4 线程之间的通信

举例,点击控制器的 View,在子线程中从网上下载一张图片,下载完毕后在主线程中更新按钮的图片。

//1 获取全局并发队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//2 异步方式下载图片dispatch_async(queue, ^{    NSString *str = @"http://u1.img.mobile.sina.cn/public/files/image/600x150_img577f313ec621a.png";    NSURL *url = [NSURL URLWithString:str];    NSData  *data = [NSData dataWithContentsOfURL:url];    UIImage *image = [UIImage imageWithData:data];    //3 回到主线程,给 UIButton 设置图片    dispatch_async(dispatch_get_main_queue(), ^{        [self.button setImage:image forState:UIControlStateNormal];    });});

5 延时执行操作

在程序设计中,延时执行的方式有三种

5.1 sleep

想要做延时操作,此种方式只需要在延时执行的操作之前执行如下代码即可

//使当前线程睡眠3秒钟后再次执行[NSThread sleepForTimeInterval:3]

缺点:会卡死当前调用的线程,整个线程会停滞。如果卡死的是主线程,那么意味着 UI 会受很大的影响。

5.2 performSelectorAfter

//当3秒后,执行 download 方法。3秒后在主线程中执行该操作[self performSelector:@selector(download) withObject:nil afterDelay:3]

5.3 GCD

可以根据队列的类型,决定延时后的操作在哪个线程中执行,可以指定在全局并发队列中执行,也可以指定在主队列中执行。

//dispatch_queue_t queue = dispatch_get_main_queue();dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{    NSLog(@"延时操作---%@",[NSThread currentThread]);});

6 一次性代码

某个代码段,在程序运行过程中,不管调用多少次,实际上改代码只执行了一次,这就是一次性代码
比如说:点击界面后,开始下载图片。如果下载图片的代码不是一次性代码的话,每点击一次界面,都需要再次下载,显然不符合实际情况。使用其他方式也可以实现(定义变量或者定义标识),但一次性代码是最简单的操作。

static dispatch_once_t oneceToken;dispatch_once(&onceToken,^{    //想要只执行一次的代码    //.....});

7 队列组

队列组就是一个对象,内部包含了队列,当队列中的任务执行完毕后,会自动调用响应的方法,这就是队列组。

需求:
需要从网络上下载一张图片,然后再下载一张 logo 图片,用 logo 做水印。如果按照顺序,先下载图片,再下载 logo,最后进行图片水印叠加,会比较耗时。最好的办法是 两张图片分别放到两个线程中进行,当两个图片全部下载好之后,再进行组装。               
实现思路:
采用队列组,手动创建一个队列组,使用dispatch_group_async(group,queue,^{})来开辟新线程执行下载操作。当队列组中的队列任务全部结束之后,会自动调用 dispatch_group_notify(queue,^{})函数,也就是说我们可以将合并水印的操作放在这个函数中进行。
示例代码
//1 获取全局并发队列dispatch_queue_t queue  = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY——DEFAULT,0);//2 创建队列组--如果是 MRC 环境中,需要 release操作: dispatch_release(group);dispatch_group_t group = dispatch_group_create(); //3 下载图片1__block UIImage *image1 = nil; //加__block 修饰是为了 block 中可以访问该变量dispatch_group_async(group,queue,^{    NSString *str = @"http://image1.png";    NSURL *url = [NSURL urlWithString:str];    NSData *data = [NSData dataWithContentsOfURL:url];    image1 = [UIImage imageWithData:data];});//4 下载图片2__block UIImage *image2 = nil; //加__block 修饰是为了 block 中可以访问该变量dispatch_group_async(group,queue,^{    NSString *str = @"http://image2.png";    NSURL *url = [NSURL urlWithString:str];    NSData *data = [NSData dataWithContentsOfURL:url];    image2 = [UIImage imageWithData:data];});//5 合并图片--group中所有队列的任务执行完之后,自动调用下面的函数dispatch_group_notify(group,queue,^{    //5.1 开启当前图形上下文    UIGraphicsBeginImageContextWithOptions(image1.size,NO,0.0);    //5.2 将 image1画在上下文    [image1 drawInRect:CGRectMake(0,0,image1.size.width,image1..size.height)];    //5.3 将 image2画在上下文    [image2 drawInRect:CGRectMake(0,0,100.50)];    //5.4 获取当前上下文的图片    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();    //5.6 回到主线程,刷新 UI    dispatch_async(dispath_get_main_queue,^{        self.imageView.image = image;    });});
1 0
原创粉丝点击