iOS多线程——GCD篇
什么是GCD
GCD是苹果对多线程编程做的一套新的抽象基于C语言层的API,结合Block简化了多线程的操作,使得我们对线程操作能够更加的安全高效。
在GCD出现之前Cocoa框架提供了NSObject类的
performSelectorInBackground:withObject
performSelectorOnMainThread
方法来简化多线程编程技术。
GCD可以解决以下多线程编程中经常出现的问题:
1.数据竞争(比如同时更新一个内存地址)
2.死锁(互相等待)
3.太多线程导致消耗大量内存
在iOS中,如果把需要消耗大量时间的操作放在主线程上面,会妨碍主线程中被称为RunLoop的主循环的执行,从而导致不能更新用户界面、应用程序的画面长时间停滞等问题。
Dispatch Queue
Dispatch Queue是GCD中对于任务的抽象队列(FIFO)执行处理。
queue分为两种,
SERIAL_DISPATCH_QUEUE 等待现在执行中处理结束
CONCURRENT_DISPATCH_QUEUE 不等待现在执行中处理结束
换句话说也就是 SERIAL_DISPATCH_QUEUE 是串行,CONCURRENT_DISPATCH_QUEUE是并行。
具体到线程上,就是SERIAL_DISPATCH_QUEUE只会创在一个线程来处理任务序列,而CONCURRENT_DISPATCH_QUEUE则会创在多个线程,但是具体创建多少个则是有运行的操作系统根据资源决定的。
所以SERIAL_DISPATCH_QUEUE 中处理的代码是有序的,而CONCURRENT_DISPATCH_QUEUE中则是无序的,但是相对会更高效一点。
API
dispatch_queue_create
用于创建一个任务执行queue
参数列表
const char *label queue的名称,作为该queue的唯一标示,改名会在Xcode和Instruments的调试器中直接作为DispatchQueue名称显示出来
dispatch_queue_attr_t 设定queue的类型,即ConcurrentQueue还是SerialQueue,NULL则默认为SerialQueue
返回值
dispatch_queue_t变量
这里要说一下main_dispatch_queue 和 global_dispatch_queue 这两种系统提供的,
main_queue通过
dispatch_get_main_queue()
global_queue通过
dispatch_get_global_queue(),global等级分为
HIGH、DEFAULT、LOW、BACKGROUND四种
dispatch_async
向指定的queue中添加block操作,异步的执行,屏蔽了多线程的实现细节,自动为我们生成线程执行。
dispatch_after
类似延迟函数,可以指定queue来进行延迟操作
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"等待3秒");
});
dispatch_group_notify
对于监听queue的执行,当所有任务完成后可以进行回调操作
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"");
});
dispatch_group_async(group, queue, ^{
NSLog(@"");
});
dispatch_group_async(group, queue, ^{
NSLog(@"");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"finish");
});
对于一系列的block在同一queue中执行,如果是serialQueue是顺序进行的,因此可以在最后一个任务来处理结束操作。但是对于concurrentQueue是并行的,如果想监听完结操作,就要用该方法。
dispatch_group_wait和notify差不多,只不过wait方法可以设置等待时间。如果时间到了还没有结束queue的所有操作,那么接下来还是会继续进行,不过还是可以设定为forever一直等待下去,这样就和notify起到一样的作用。
dispatch_barrier_async
该操作主要是为了防止资源竞争。在concurrentQueue中,所有block无序的按照所创建的线程数量同时进行。如果在concurrentQueue中有两个写入操作,而且他都是读取操作,这时两个写入操作间就会出现资源竞争,而读取操作则会读取脏数据。所以对于在concurrentQueue中不能够与其它操作并行的block就需要使用dispatch_barrier_async方法来防止资源竞争。
dispatch_sync
和dispatch_async不同,dispatch_sync用于线程之间的同步操作,比如说A线程要做一件事必须要放在B线程之后来进行,那么此时就需要用到dispatch_sync。
另外,不能够在某个执行线程中同步自己,这样会造成线程死锁,比如说
dispatch_queue_t queue1 = dispatch_get_main_queue();
dispatch_sync(queue1, ^{
NSLog(@"main queue 中同步main queue操作");
}); dispatch_queue_t queue = dispatch_queue_create("com.queue.www", NULL);
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"在新的serial queue中同步serial queue操作");
});
});
所以说使用serial queue的时候一定不要同步自己。
dispatch_apply
dispatch_apply函数是dispatch_sync和dispatch group的关联函数,是用指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束,例如
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_apply(, queue, ^(size_t index) {
NSLog(@"%ld",index);
});
NSLog(@"apply finish"); -- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:]
-- ::18.296 Dispatch[:] apply finish
实际上可以看出来,该函数让主线程和queue进行同步操作,并且等queue中所有线程执行完毕后才继续执行。
dispatch_semaphore
在进行数据处理时,dispatch_barrier_async可以避免这类问题,但是有时需要更加精细的操作。
比如要对数组添加10000个对象,用concurrentQueue添加。我们知道concurrentQueue会生成多个线程,很可能会出现多个线程一起对数组访问的情况,很容易出现问题。我们需要控制一次只让一个线程操作数组,如下:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_semaphore_t semaphore = dispatch_semaphore_create(); NSMutableArray *array = [NSMutableArray new];
for (int i = ; i < ; i++)
{
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject:[NSNumber numberWithInt:i]];
NSLog(@"add %d",i);
dispatch_semaphore_signal(semaphore);
});
}
这里简单说一下信号量,也就是创建dispatch_semaphore的第二个参数。指定一个信号量,那么当信号量是大于0的时候所有线程都是可访问的。一旦有现成访问信号量会减1,如果信号量为0就会进入等待,知道dispatch_semaphore_signal函数调用来重新恢复信号量。所以基本上可以理解为有几个信号量就能有几个线程并发的访问。
再比如说现在有两个线程一个添加数据一个删除数据,那么就需要两个信号量变量来实现多线程间的协作
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
dispatch_semaphore_t semaphoreAdd = dispatch_semaphore_create();
dispatch_semaphore_t semaphoreRemove = dispatch_semaphore_create();
NSMutableArray *array = [NSMutableArray new];
for (int i = ; i < ; i++)
{
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphoreAdd, DISPATCH_TIME_FOREVER);
[array addObject:[NSNumber numberWithInt:i]];
NSLog(@"add %lu",[array count]);
dispatch_semaphore_signal(semaphoreRemove);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphoreRemove, DISPATCH_TIME_FOREVER);
[array removeObject:[NSNumber numberWithInt:i]];
NSLog(@"add %lu",[array count]);
dispatch_semaphore_signal(semaphoreAdd);
});
}
dispatch_once
dispatch_once用来标记一个操作,只执行一次,该方法一般在生产单例对象使用。如果不用dispatch_once创建单例是不安全的,需要进行加锁处理,但是dispatch_once可以很好地解决这一点。
+(instancetype)sharedInstance
{
static CustomObject *obj;
static dispatch_once_t once;
dispatch_once(&once, ^{
obj = [[CustomObject alloc] init];
});
return obj;
}
iOS多线程——GCD篇的更多相关文章
- iOS多线程 GCD
iOS多线程 GCD Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法. dispatch queue分成以下三种: 1)运行在主线程的Main que ...
- iOS 多线程GCD的基本使用
<iOS多线程简介>中提到:GCD中有2个核心概念:1.任务(执行什么操作)2.队列(用来存放任务) 那么多线程GCD的基本使用有哪些呢? 可以分以下多种情况: 1.异步函数 + 并发队列 ...
- iOS 多线程 GCD part3:API
https://www.jianshu.com/p/072111f5889d 2017.03.05 22:54* 字数 1667 阅读 88评论 0喜欢 1 0. 预备知识 GCD对时间的描述有些新奇 ...
- ios多线程-GCD基本用法
ios中多线程有三种,NSTread, NSOperation,GCD 这篇就讲讲GCD的基本用法 平时比较多使用和看到的是: dispatch_async(dispatch_get_global_q ...
- iOS多线程——GCD与NSOperation总结
很长时间以来,我个人(可能还有很多同学),对多线程编程都存在一些误解.一个很明显的表现是,很多人有这样的看法: 新开一个线程,能提高速度,避免阻塞主线程 毕竟多线程嘛,几个线程一起跑任务,速度快,还不 ...
- iOS多线程GCD的使用
1. GCD 简介 Grand Central Dispatch(GCD)是异步执行任务的技术之一.一般将应用程序中记述的线程管理用的代码在系统级中实现.开发者只需要定义想执行的任务并追加到适当的Di ...
- iOS多线程GCD详解
在这之前,一直有个疑问就是:gcd的系统管理多线程的概念,如果你看到gcd管理多线程你肯定也有这样的疑问,就是:并发队列怎么回事,即是队列(先进先出)怎么会并发,本人郁闷了好久,才发现其实cgd管理多 ...
- iOS多线程GCD的简单使用
在iOS开发中,苹果提供了三种多线程技术,分别是: (1)NSThread (2)NSOperation (3)GCD 简单介绍一下GCD的使用. GCD全称 Grand Central Dispat ...
- iOS多线程GCD简介(一)
之前讲过多线程之NSOperation,今天来讲讲代码更加简洁和高效的GCD.下面说的内容都是基于iOS6以后和ARC下. Grand Central Dispatch (GCD)简介 Grand C ...
随机推荐
- zoj 3314 CAPTCHA(纯模拟)
题目 有些人用深搜写的,当然我这弱弱的,只理解纯模拟... 纯模拟,第一次写了那么长的代码,我自己也是够坚韧不拔的,,,,必须留念啊!!! 注意,G包含C,E包含L,R包含P,(照图说O应该不包含C, ...
- 洛谷 2387 NOI2014魔法森林 LCT
[题解] 我们先把边按照$a$值从小到大排序,并按照这个顺序加边. 如果当前要加入的边连接的两点$u$与$v$已经是连通的,那么直接加入这条边就会出现环.这时我们需要删除这个环中$b$值最大的边.因此 ...
- Navicat premium连接Oracle报ORA-28547错误
1:ORA-28547 原因:navicate Primium版本的OCi和本地数据库的OCI版本不一致. 解决方法: 1:把navicate Primium版本自带oci.dll替换本地Oracle ...
- grafana简介
grafana一般是和一些时间序列数据库进行配合来展示数据的,例如:Graphite.OpenTSDB.InfluxDB等 grafana是用于可视化大型测量数据的开源程序,他提供了强大和优雅的方式去 ...
- [Codeforces 872]比赛记录
强行打了$cf$上的第一场比赛,现在感觉自己的$rating$会炸飞= = A 这是练习输入输出吗QAQ,竟然$WA$了两遍QAQ,我$WA$的一声就哭了出来啊QAQ B 好像很水的乱扫就好了,m ...
- R语言为数据框添加列名或行名
1.添加列名 wts=c(1,1,1) names(wts)=c("setosa","versicolor","virginica") 2. ...
- code vs 2166 Bessie的体重问题
2166 Bessie的体重问题 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold 题解 题目描述 Description Bessie像她的诸多姊妹一 ...
- cogs 7. 通信线路
7. 通信线路 ★★ 输入文件:mcst.in 输出文件:mcst.out 简单对比时间限制:1.5 s 内存限制:128 MB 问题描述 假设要在n个城市之间建立通信联络网,则连通n ...
- OpenCV摄像头读取
在Mac下面使用默认的OpenCV读取摄像头程序会报错 int main(int, char**) { VideoCapture cap(0); // open the default camera ...
- applicationcontext理解使用
Spring ApplicationContext 容器 Application Context 是 spring 中较高级的容器.和 BeanFactory 类似,它可以加载配置文件中定义的 bea ...