iOS 多线程之 GCD 的基本使用

什么是GCD

全称Grand Central Dispatch 中暑调度器 纯C语言 提供了很多强大的函数

GCD 的优势

GCD是苹果公司为多核的并行运算提出的解决方案

GCD会自动利用更多的CPU内核(比如 双核 四核)

GCD会自动管理线程的生命周期 (创建线程 调度任务 销毁线程)

程序员只需要告诉GCD想要执行什么任务 不需要编写任何线程管理的代码

GCD的核心概念任务和队列

任务: 需要执行的操作

队列:用来存放任务 调度任务 安排任务在哪个线程中执行

GCD的使用步骤

1 定制任务 确定想要做的事情

2 将任务添加到队列中 GCD会自动的将队列中的任务取出 放到对应的线程中执行 任务的取出遵循队列的FIFO原则 先进先出 后今后出

GCD中执行任务的常用方式

用同步的方式来执行任务

dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);

用异步的方式执行任务

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

同步和异步的区别

同步:只能在当前线程中执行任务 不具备开启线程的能力

异步:  可以在新的线程中执行任务 具备开启线程的能力

GCD中队列的类型

队列的作用 存放任务 并安排任务在相应的线程中执行

并发队列 可以让多个任务同时执行任务 (自动开启多个线程同时执行任务)

并发功能只有在异步(dispatch_async)函数下才有效

串行队列 让任务一个接着一个执行(一个任务执行完毕后 在执行下一个任务)

所以

同步和异步主要影响 能不能开启新线程

同步 只是在当前线程中执行任务 不具备开启新线程的能力

异步 可以在新的线程中执行任务 具备开启新线程的能力

并行和串行 影响的是 任务的执行方式

并发 允许多个任务同时执行

串行  一个任务执行完毕后 再执行下一个任务

GCD 的基本使用

异步函数 + 并发队列  如果队列中有多个任务会开启多条的线程 任务的执行没有顺序(并发执行或者叫异步执行)

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self asyncConcurrent];
}
//异步函数 + 并发队列 会开启多条线程 队列中的任务异步执行 没有顺序
- (void)asyncConcurrent {
    //1 创建队列
    /*
     *第一个参数 C语言的字符串 标签
     *第二个参数 是队列的类型DISPATCH_QUEUE_CONCURRENT 并发队列 DISPATCH_QUEUE_SERIAL串行队列
     */
    dispatch_queue_t queue = dispatch_queue_create("com.tian.download", DISPATCH_QUEUE_CONCURRENT);
    //第二步 封装任务->添加任务到队列中 在Block中封装任务
    dispatch_async(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });
}

2018-03-06 22:14:48.892107+0800 GCDDemo[941:42730] download3 --- <NSThread: 0x60400026fe40>{number = 5, name = (null)}
2018-03-06 22:14:48.892107+0800 GCDDemo[941:42518] download1 --- <NSThread: 0x60800026bbc0>{number = 3, name = (null)}
2018-03-06 22:14:48.892107+0800 GCDDemo[941:42728] download2 --- <NSThread: 0x60800026ba40>{number = 4, name = (null)}

异步函数 + 串行队列 即使是多个任务 也只会开启一条线程(因为串行队列 任务一个接一个执行 不需要开启多个此线程) 任务的执行是顺序的(串行执行)

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
    [self asyncSerial];
}
//异步函数 + 串行队列  即使是多个任务 也只会开启一条线程 任务的执行是顺序的(串行执行)
- (void)asyncSerial {
    //1 创建队列
    dispatch_queue_t queue = dispatch_queue_create("tian", DISPATCH_QUEUE_SERIAL);

    //第二步 封装任务->添加任务到队列中 在Block中封装任务
    dispatch_async(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });

}

2018-03-06 22:26:58.083762+0800 GCDDemo[979:50891] download1 --- <NSThread: 0x60800006c340>{number = 3, name = (null)}2018-03-06 22:26:58.084028+0800 GCDDemo[979:50891] download2 --- <NSThread: 0x60800006c340>{number = 3, name = (null)}2018-03-06 22:26:58.084211+0800 GCDDemo[979:50891] download3 --- <NSThread: 0x60800006c340>{number = 3, name = (null)}

同步函数 + 并发队列  即使是多个任务也不会开启线程 所以任务是在一个线程中一个接一个的完成的 (串行执行) 按顺序完成

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
    [self syncConcurrent];
}

//同步函数 + 并发队列 不会开线程 只会在当前线程中执行 任务是串行执行的(按顺序执行的)
- (void)syncConcurrent {
    dispatch_queue_t queue = dispatch_queue_create("com.tian.download", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });
}2018-03-06 22:44:17.280207+0800 GCDDemo[1010:58942] download1 --- <NSThread: 0x60c00006f4c0>{number = 1, name = main}2018-03-06 22:44:17.280432+0800 GCDDemo[1010:58942] download2 --- <NSThread: 0x60c00006f4c0>{number = 1, name = main}2018-03-06 22:44:17.280593+0800 GCDDemo[1010:58942] download3 --- <NSThread: 0x60c00006f4c0>{number = 1, name = main}

同步函数 + 串行队列  不会开启线程 任务是串行执行

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
//    [self syncConcurrent];
    [self syncSerial];
}
//同步函数 + 串行队列
- (void)syncSerial {
    //1 创建队列
    dispatch_queue_t queue = dispatch_queue_create("tian", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"download3 --- %@",[NSThread currentThread]);
    });
}
2018-03-06 22:54:41.556594+0800 GCDDemo[1041:63951] download1 --- <NSThread: 0x60c0002601c0>{number = 1, name = main}
2018-03-06 22:54:41.556861+0800 GCDDemo[1041:63951] download2 --- <NSThread: 0x60c0002601c0>{number = 1, name = main}
2018-03-06 22:54:41.557016+0800 GCDDemo[1041:63951] download3 --- <NSThread: 0x60c0002601c0>{number = 1, name = main}

默认的并发队列

//全局并发队列
    //第一个参数 优先级 第二个参数 预留参数 传0
    /*#define DISPATCH_QUEUE_PRIORITY_HIGH 2
     *#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
     *#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
     *#define DISPATCH_QUEUE_PRIORITY_BACKGROUND 优先级最低
     */

    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //和创建的并发队列 大部分情况下是一样的 但是也有一些区别
    //一个是直接创建的 系统本身不存在的 一个是拿系统封装好的 系统中本身是存在的

并不是说有多少个任务就开启多少个线程 开启多少个线程是系统控制的。

GCD主队列的使用

获取主队列

dispatch_get_main_queue()

异步函数 + 主队列

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
//    [self syncConcurrent];
//    [self syncSerial];
    [self asyncMain];
}
//异步函数 + 主队列
//凡是放在主队列里面的任务都要在主线程里面执行
//所以没有必须要开线程
//不会开线程 任务串行执行
- (void)asyncMain {
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--1-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--2-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--3-- 主队列  %@",[NSThread currentThread]);
    });
}

同步函数 + 主队列 错误代码

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    [self asyncConcurrent];
//    [self asyncSerial];
//    [self syncConcurrent];
//    [self syncSerial];
    [self asyncMain];
}
//同步函数 + 主队列 产生死锁
/*
 *主队列调度主线程执行任务 在将封装的任务添加到主队列等待调度的时候 主队列正在执行任务(串行执行 一个任务结束才能执行另一个任务) 所以封装的任务永远等不到机会调度完成
 *如果主队列发现当前主线程有任务在执行 那么主队列会暂停调度队列中的任务 直到主线处于空闲为止
 */
- (void)syncMain {
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--1-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--2-- 主队列  %@",[NSThread currentThread]);
    });
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"异步函数--3-- 主队列  %@",[NSThread currentThread]);
    });
}

但是如果在子线程中执行上述错误代码 是不会产生死锁的 因为 syncMain这个任务是在子线程中 并没有占有主线程。

GCD线程间的通信

- (void)downLoadImageFormService {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSURL *urlStr = [NSURL URLWithString:[NSString stringWithFormat:@"*********"]];
        NSData *data = [NSData dataWithContentsOfURL:urlStr];
        UIImage *image = [UIImage imageWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });
}

GCD常用函数

延迟函数 和 一次性函数

//1 延迟函数
- (void)delay {
    NSLog(@"start");
    //延迟执行的第一种方式
    [self performSelector:@selector(task) withObject:nil afterDelay:2.0];
    //第二种方式
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:NO];
    //第三种方式
    /*
     *第一个参数 DISPATCH_TIME_NOW 从现在开始
     *第二个参数 要延迟的时间 2.0 * NSEC_PER_SEC = 2 * 10 的九次方 GCD的时间单位是纳秒 转换成秒
     *第三个参数 队列 主线程队列 在主线程执行 并发队列 在子线程里面执行
     */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self task];
    });
}
- (void)task {
    NSLog(@"延迟任务");
}
//2 一次性代码 整个应用程序只会执行一次的代码 常用在单例模式里面
- (void)once {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"整个程序我只会执行一次哦");
    });
}

栅栏函数 保证队列里任务的执行顺序 不能使用全局并发队列 否则无效

//栅栏函数 适用于需要控制队列里面任务的执行顺序
- (void)barrier {
    //栅栏函数 不能使用全局并发队列 否则无效
    dispatch_queue_t queue = dispatch_queue_create("cdownload", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"任务1 %@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏 保证顺序执行");
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏 保证任务3最后执行");
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
}

快速迭代函数

//快速迭代函数 就是遍历函数 比较快是因为会开子线程 for循环是在主线程中完成的 这就造成了遍历是无序的
- (void)apply {
    /*
     *第一个参数 要遍历的次数
     *第二个参数 队列 (只能传并发队列 主队列会死锁 串行队列 没效果)
     *size_t index 索引值
     */
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
        NSLog(@"%zu,%@",index,[NSThread currentThread]);
    });
}

GCD的队列组的使用 group

//队列组 可以监听队列组中任务的完成情况
- (void)group {
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //创建队列组
    dispatch_group_t group = dispatch_group_create();
    //使用异步函数封装任务
    //封装任务-》添加到队列-》会监听任务的执行情况通知group
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务1 %@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"任务2 %@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"任务3 %@",[NSThread currentThread]);
    });
    //当队列组中所有的任务都执行完毕的时候 会进入到下面的方法
    //但是这个方法也是异步的 不是阻塞的
    dispatch_group_notify(group, queue, ^{
        NSLog(@"组中的任务都执行完了");
    });
}

- (void)group1 {//以前的写法
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //创建队列组
    dispatch_group_t group = dispatch_group_create();
    //在该方法后面的异步任务 会被纳入到队列组的监听范围内
    //enter 和 leave 必须要配对使用
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"download1 --- %@",[NSThread currentThread]);
        //当任务执行完毕 退出队列组
        dispatch_group_leave(group);
    });
    dispatch_async(queue, ^{
        NSLog(@"download2 --- %@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, queue, ^{
        NSLog(@"任务已经全部完成");
    });
//    //等到队列组中所有的任务都执行完毕之后才能执行 本身是阻塞的 这行代码不执行 下面的代码也不会执行 与dispatch_group_notify效果等同
//    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//    NSLog(@"任务已经被执行完毕");
}
- (void)demo {
    //下载图片 下载图片 合并图片
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group,queue, ^{
        NSURL *urlStr = [NSURL URLWithString:[NSString stringWithFormat:@"*********"]];
        NSData *data = [NSData dataWithContentsOfURL:urlStr];
        self.image1 = [UIImage imageWithData:data];
    });
    dispatch_group_async(group,queue, ^{
        NSURL *urlStr = [NSURL URLWithString:[NSString stringWithFormat:@"*********"]];
        NSData *data = [NSData dataWithContentsOfURL:urlStr];
        self.image2 = [UIImage imageWithData:data];
    });
    dispatch_group_notify(group, queue, ^{
        //合并图片
        //创建图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        //画图1
        [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
        //画图2
        [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
        //根据上下文得到一张图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        //关闭上下文
        UIGraphicsEndImageContext();
        //更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
    });

}

原文地址:https://www.cnblogs.com/huanying2000/p/8520041.html

时间: 2024-08-29 12:14:49

iOS 多线程之 GCD 的基本使用的相关文章

iOS多线程技术—GCD介绍

iOS多线程技术—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 GCD是苹果公司为多核的并行运算提出的解决方案 GCD会自动利用更多的CPU内核(比如双核.四核) GCD会自动管理线程的生命周期(创建线程.调度任务.销毁线程) 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码 3.提示 (1)GCD存在于libdispatch.dylib这个库中,

iOS多线程技术—GCD的用法

iOS多线程技术—GCD的用法 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程. 获取主队列的方式: 1 // 2 // YYViewController.m 3 // 12-GCD的基本使用(主队列) 4 // 5 // Created by 孔医己 on 14-6-25. 6 // Copyright (c) 2014

iOS多线程 NSThread/GCD/NSOperationQueue

http://www.cnblogs.com/kenshincui/p/3983982.html iOS开发系列--并行开发其实很容易 2014-09-20 23:34 by KenshinCui, 9738 阅读, 19 评论, 收藏,  编辑 --多线程开发 概览 大家都知道,在开发过程中应该尽可能减少用户等待时间,让程序尽可能快的完成运算.可是无论是哪种语言开发的程序最终往往转换成汇编语言进而解释成机器码来执行.但是机器码是按顺序执行的,一个复杂的多步操作只能一步步按顺序逐个执行.改变这种

iOS多线程开发——GCD的使用与多线程开发浅析(二)

对于iOS多线程开发,我们时刻处于学习之中,在看书中,看文档中,项目开发中,都可以去提高自己.最近刚看完了<Objective-C高级编程 iOS与OS X多线程和内存管理>这本书后,对多线程有了更为深入的理解,故在此做一个总结与记录.这本书我已经上传至网盘  https://pan.baidu.com/s/1c2fX3EC ,这本书是iOS开发者必读的书之一,写得很不错,欢迎大家下载阅读.书的封面如下,故也称狮子书: . (1)多线程会遇到的问题 . 多线程会出现什么问题呢?当多个线程对同一

iOS多线程(GCD NSOperation NSThread)

进程:进程是指在系统中正在运行的一个应用程序,每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 线程:1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程)线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行,比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行.1个线程中任务的执行是串行的,如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务,也就是说,在同一时间内,1个线程只能执行1个任务,比如在1个线程中下载3个文件(分别是文

iOS 多线程编程gcd全面系统认识

这两天在看<OC高级编程-多线程编程和内存管理>日本人写的那本,该书对arc,block和gcd有了更深层次的解读,非常不错.现在总结一下gcd相关的知识.有关arc和block的参考arc   参考block 网上很多博客都对gcd有过讲解,很多是对gcd的全局队列,主线程队列,创建队列等等,做了单方面的描述,不是很全面系统.下面我们将学习一下系统得gcd.本文主要分为下面几个要点,前几个好点比较好理解,最后可能理解起来有些费劲! ● 什么是gcd,iOS为什么要用多线程 ● 创建线程,序列

ios 多线程小结----- GCD篇

//3 GCD(充分利用设备的多盒)-------------屏蔽了线程,只能看见任务 队列步骤两步,定制任务,将任务添加到队列.GCD将添加的任务,放到线程中去执行,自动执行,自动释放原则:先进先出,先添加的任务先执行,有别于栈的先进后出,先添加的任务后执行 -------GCD--- GCD有两个用来执行任务的函数:同步函数,异步函数同步:按顺序执行任务,同一条线程执行(不开线程)异步:同时执行任务(开启新线程) 同步,异步,并发,串行同步:当前线程执行,不具备开启新线程的能力异步:在新线程

iOS多线程技术---GCD

下面这个链接的GCD讲的很好,做个备忘. 1,将GCD的各种原理,分析的比较透彻: http://www.dreamingwish.com/dream-2012/gcdgrand-central-dispatch%E6%95%99%E7%A8%8B.html 2,唐巧(前网易有道员工),简洁明了的使用示例: http://blog.devtang.com/blog/2012/02/22/use-gcd/

ios多线程开发 GCD常见用法

1-延迟执行 可以安排其线程(1),主队列 dispatch_queue_t queue= dispatch_get_main_queue(); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{ NSLog(@"主队列--延迟执行------%@",[NSThread currentThread]); }); 可以安排其线程(2),并发队列 1.获取全局并发