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]);
});
} -- ::48.892107+ GCDDemo[:] download3 --- <NSThread: 0x60400026fe40>{number = , name = (null)}
-- ::48.892107+ GCDDemo[:] download1 --- <NSThread: 0x60800026bbc0>{number = , name = (null)}
-- ::48.892107+ GCDDemo[:] download2 --- <NSThread: 0x60800026ba40>{number = , 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]);
});
}
-- ::41.556594+ GCDDemo[:] download1 --- <NSThread: 0x60c0002601c0>{number = , name = main}
-- ::41.556861+ GCDDemo[:] download2 --- <NSThread: 0x60c0002601c0>{number = , name = main}
-- ::41.557016+ GCDDemo[:] download3 --- <NSThread: 0x60c0002601c0>{number = , 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, );
//和创建的并发队列 大部分情况下是一样的 但是也有一些区别
//一个是直接创建的 系统本身不存在的 一个是拿系统封装好的 系统中本身是存在的
并不是说有多少个任务就开启多少个线程 开启多少个线程是系统控制的。
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, ), ^{
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(, dispatch_get_global_queue(, ), ^(size_t index) {
NSLog(@"%zu,%@",index,[NSThread currentThread]);
});
}
GCD的队列组的使用 group
//队列组 可以监听队列组中任务的完成情况
- (void)group {
dispatch_queue_t queue = dispatch_get_global_queue(, );
//创建队列组
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(, );
//创建队列组
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(, );
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(, ));
//画图1
[self.image1 drawInRect:CGRectMake(, , , )];
//画图2
[self.image2 drawInRect:CGRectMake(, , , )];
//根据上下文得到一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
//更新UI
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
}); }
iOS 多线程之 GCD 的基本使用的更多相关文章
- [iOS]多线程和GCD
新博客wossoneri.com 进程和线程 进程 是指在系统中正在运行的一个应用程序. 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 比如同时打开QQ.Xcode,系统就会分别 ...
- iOS多线程 NSThread/GCD/NSOperationQueue
无论是GCD,NSOperationQueue或是NSThread, 都没有线程安全 在需要同步的时候需要使用NSLock或者它的子类进行加锁同步 "] UTF8String], DISPA ...
- iOS 多线程 之 GCD(大中枢派发)(一)
导语: 本文个人原创,转载请注明出处(http://www.cnblogs.com/pretty-guy/p/8126981.html) 在iOS开发中多线程操作通常是一下3种,本文着重介绍Dispa ...
- IOS多线程(GCD)
简介 Grand Central Dispatch 简称(GCD)是苹果公司开发的技术,以优化的应用程序支持多核心处理器和其他的对称多处理系统的系统.这建立在任务并行执行的线程池模式的基础上的.它首次 ...
- ios 多线程小结----- GCD篇
//3 GCD(充分利用设备的多盒)-------------屏蔽了线程,只能看见任务 队列步骤两步,定制任务,将任务添加到队列.GCD将添加的任务,放到线程中去执行,自动执行,自动释放原则:先进先出 ...
- IOS 多线程 NSOperation GCD
1.NSInvocationOperation NSInvocationOperation * op; NSOperationQueue * que = [[NSOperationQueuealloc ...
- iOS 多线程 之 GCD(大中枢派发)(二)
本文接着上一篇讲.主要讲:dispatch_source. dispatch_source主要用户监听事件,可以监听如下事件 DISPATCH_SOURCE_TYPE_DATA_ADD DISPATC ...
- iOS 多线程:『GCD』详尽总结
本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法.这大概是史上最详细.清晰的关于 GCD 的详细讲解+总结的文章了.通过本文,您将了解到: 1. GCD 简介 2. GCD 任务和队列 ...
- iOS 多线程 GCD part3:API
https://www.jianshu.com/p/072111f5889d 2017.03.05 22:54* 字数 1667 阅读 88评论 0喜欢 1 0. 预备知识 GCD对时间的描述有些新奇 ...
随机推荐
- android开发中,在java中怎样使用c提供过来char*
这个char*假设是一般的字符串的话,作为string传回去就能够了.假设是含有'\0'的buffer,最好作为bytearray传出,由于能够制定copy的length.假设copy到string, ...
- 怎样启动JDBC Debug模式,打印JDBC诊断日志
1.下载Debug版本号jar包 首先要下载一个Debug版本号的JDBC jar包,Debug版本号的jar包命名形式为jdbcX_g.jar(例如以下图所看到的).如Oracle11g的 ...
- class文件简介及加载
Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件,取出二进制数据,加载到内存中, ...
- UML类图详解_泛化关系
泛化其实就是继承关系,还是比较简单的,那么我们就把之前有些问题的博客UML类图重新来实现一次. 依旧是这个图 下面我们来看一个例子 Account.h #include <cstdlib> ...
- iconv c 代码学习
(struct stringpool_t *)0)->stringpool_str15 含义为: 1.创建一个结构体stringpool_t 指针(struct stringpool_t *) ...
- 在CPU上运行Tensorflow
如果你是用的GPU版本的Tensorflow,你可以这样来使用CPU版本的Tensorlfow: config = tf.ConfigProto( device_count = {'GPU': 0} ...
- python 类成员的修饰符
类的所有成员在上一步骤中已经做了详细的介绍,对于每一个类的成员而言都有两种形式: 公有成员,在任何地方都能访问 私有成员,只有在类的内部才能方法 私有成员和公有成员的定义不同:私有成员命名时,前两个字 ...
- Linux命令之umask
一 权限掩码umask umask是chmod配套的,总共为4位(gid/uid,属主,组权,其它用户的权限),不过通常用到的是后3个,例如你用chmod 755 file(此时这文件的权限是属主读( ...
- src.rpm包安装方法
有些软件包是以.src.rpm结尾的,这类软件包是包含了源代码的rpm包,在安装时需要进行编译.这类软件包有多种安装方法,以redhat为例说明如下: 注意: 如果没有rpmbuild可以从系统安装光 ...
- 关于 SQLNET.AUTHENTICATION_SERVICES 验证方式的说明
今天去客户那里巡检,客户提出为了提高数据库安全性考虑,须要改动sys/systempassword,并通过数据库验证方式来代替默认的操作系统方式,如今我来把这两种验证方式总结一下. 操作系统验证.即通 ...