iOS多线程——GCD
最近的项目遇到了很多多线程的问题,借此机会对GCD进行了一番学习并总结。首先说一下什么是GCD,GCD全称 Grand Central Dispatch,是异步执行任务的技术之一。开发者只需要定义想要执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务。说到多线程,我们有必要了解一下GCD的两个核心概念。
一、任务和队列
(1)任务:执行什么操作
(2)队列:用来存放任务
将任务添加到队列中,GCD会自动将队列中的任务取出放到对应的线程中执行,遵循FIFO原则,先进先出,后进后出。
队列分为串行和并行,任务的执行又分为同步和异步。两两组合,串行同步,串行异步,并行同步,并行异步。而异步是多线程的代名词,异步在实际引用中会开启新的线程执行耗时操作。队列只负责任务的调度,而不负责任务的执行,任务是在线程中执行。
二、同步异步,串行并行
(1)同步异步指的是能否开启新的线程。同步不能开启新的线程,异步可以开启。
(2)串行并行指的是任务的执行方式。串行是指多个任务时,各个任务按顺序执行,完成一个后才能进行下一个。并行是指多个任务可以同时执行。(异步是多个任务并行的前提条件!)
三、GCD的API
(1)Main Dispatch Queue
是在主线程中执行任务的Dispatch Queue。因为主线程只有1个,所以Main Dispatch Queue是Serial Dispatch Queue。追加到Main Dispatch Queue中的任务将在主线程的RunLoop中执行。因为是在主线程中执行,所以应该只将用户界面更新等一些必须在主线程中执行的任务追加到Main Dispatch Queue中。
(2)Global Dispatch Queue
是所有应用程序都能使用的Concurrent Dispatch Queue。大多数情况下,可以不必通过dispatch_queue_create函数生成Concurrent Dispatch Queue,而是只要获取Global Dispatch Queue使用即可。Global Dispatch Queue有4个优先级,分别是:High、Default、Low、Background。
(3)dispatch_set_target_queue
dispatch_queue_t mySerailDispatchQueue = dispatch_queue_create("com.example.MySerailDispatchQueue", NULL);
dispatch_queue_t globalDispatchQueueBackground = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, );
dispatch_set_target_queue(mySerailDispatchQueue, globalDispatchQueueBackground);
// 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样
一般都是把一个任务放到一个串行的queue中,如果这个任务被拆分了,被放置到多个串行的queue中,但实际还是需要这个任务同步执行,那么就会有问题,因为多个串行queue之间是并行的。
dispatch_queue_t mySerailDispatchQueue = dispatch_queue_create("com.example.MySerailDispatchQueue", DISPATCH_QUEUE_SERIAL); dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue3 = dispatch_queue_create("test.3", DISPATCH_QUEUE_SERIAL); dispatch_set_target_queue(queue1, mySerailDispatchQueue);
dispatch_set_target_queue(queue2, mySerailDispatchQueue);
dispatch_set_target_queue(queue3, mySerailDispatchQueue); dispatch_async(queue1, ^{
NSLog(@"1 in");
[NSThread sleepForTimeInterval:.f];
NSLog(@"1 out");
}); dispatch_async(queue2, ^{
NSLog(@"2 in");
[NSThread sleepForTimeInterval:.f];
NSLog(@"2 out");
});
dispatch_async(queue3, ^{
NSLog(@"3 in");
[NSThread sleepForTimeInterval:.f];
NSLog(@"3 out");
});
输出结果:
-- ::59.424 GCD-test[:] in
-- ::02.427 GCD-test[:] out
-- ::02.427 GCD-test[:] in
-- ::04.431 GCD-test[:] out
-- ::04.432 GCD-test[:] in
-- ::05.437 GCD-test[:] out
通过打印的结果说明我们设置了queue1和queue2队列以targetQueue队列为参照对象,那么queue1和queue2中的任务将按照targetQueue的队列处理。
适用场景:
一般都是把一个任务放到一个串行的queue中,如果这个任务被拆分了,被放置到多个串行的queue中,但实际还是需要这个任务同步执行,那么就会有问题,因为多个串行queue之间是并行的。这时候dispatch_set_target_queue将起到作用。
(4)dispatch_after
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull *NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at least three seconds.");
});
该方法的第一个参数time,是指定时间用的dispatch_time_t类型的值,该值使用dispatch_time函数或dispatch_walltime函数生成。
dispatch_time函数能够获取从第一个参数指定的时间开始到第二个参数指定的毫微秒单位时间后的时间。DISPATCH_TIME_NOW表示现在的时间。dispatch_time函数通常用于计算相对时间,而dispatch_walltime函数用于计算绝对时间。
第二个参数dispatch queue,第三个参数要执行的block。需要注意的是,dispatch_after函数并不是在指定时间后执行处理,而只是在指定时间追加处理到dispatch queue。此代码与3秒后用dispatch_async函数追加block到Main Dispatch queue相同。
下面举一个简单的例子,看一下dispatch_after的执行效果:
- (void)dispatch_after
{
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 10ull *NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"hello");
});
_count = ;
_timer = [NSTimer timerWithTimeInterval: target:self selector:@selector(run) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
} - (void)run{
if (_count == ){
[_timer invalidate];
}
_count ++;
NSLog(@"time: %ld",(long)_count);
}
-- ::27.015 GCD-test[:] time:
-- ::28.015 GCD-test[:] time:
-- ::29.014 GCD-test[:] time:
-- ::30.014 GCD-test[:] time:
-- ::31.015 GCD-test[:] time:
-- ::32.015 GCD-test[:] time:
-- ::33.015 GCD-test[:] time:
-- ::34.014 GCD-test[:] time:
-- ::35.015 GCD-test[:] time:
-- ::36.014 GCD-test[:] time:
-- ::36.015 GCD-test[:] hello
-- ::37.014 GCD-test[:] time:
(5)dispatch group
在追加到dispatch queue中的多个处理全部结束后想执行结束处理,这种情况会经常出现,当然,我们可以使用一个serial dispatch queue,将想要执行的处理全部追加到该dispatch queue中,在最后追加结束处理即可实现,但是在使用concurrent dispatch queue时或者同时使用多个dispatch queue时,就会变得颇为复杂,这个时候我们可以使用dispatch group,无论向什么样的dispatch queue中追加处理,使用dispatch group 都可监视这些处理执行的结束。一旦检测到所有处理执行结束,就可将结束的处理追加到dispatch queue中,这就是使用dispatch queue的原因。
先上代码:
- (void)dispatch_group
{
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(@"blk0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk2");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
}
-- ::49.038 GCD-test[:] blk0
-- ::49.038 GCD-test[:] blk2
-- ::49.038 GCD-test[:] blk1
-- ::49.045 GCD-test[:] done
dispatch_group_async函数与dispatch_async函数相同,都追加到Block到指定的dispatch queue中。不同的是指定生成的dispatch group为第一个参数,指定的Block属于指定的dispatch group。
在追加到dispatch group中的处理全部执行结束时,上面的源代码中使用dipatch_group_notify函数会将执行的block追加到dispach queue中,将第一个参数指定为要监视的dispatch group,在追加到该dispatch group的处理全部结束时,将第三个参数的block追加到第二个参数指定的dispatch queue。另外需要强调的是在dispatch_group_notify函数中不管制定了什么样的dispatch queue,属于dispatch group的全部处理在追加指定的block时都已经执行结束。
另外dispatch group中还可以使用dispatch_group_wait函数仅等待全部处理执行结束,还是直接上代码:
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(@"blk0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk2");
});
// dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull *NSEC_PER_SEC);
long result = dispatch_group_wait(group, time);
if (result == ){
/*
* 属于dispatch group的全部处理执行结束
*/
NSLog(@"done");
}else{
/*
* 属于dispatch group的处理为全部结束
*/
NSLog(@"unfinished");
}
-- ::47.378 GCD-test[:] blk1
-- ::47.378 GCD-test[:] blk2
-- ::47.378 GCD-test[:] blk0
-- ::47.378 GCD-test[:] done
如果dispatch_group_wait返回值不为0,就意味着虽然经过了指定的时间,但属于dispatch group的某一个处理还在进行中,你可以尝试将NSEC_PER_SEC改为NSEC_PER_USEC,这里不做详细介绍了。如果返回值为0,那么全部处理执行结束。如果等待时间为DISPATCH_TIME_FOREVER返回值恒为0,
在主线程的runloop的每次循环中,可检查执行是否结束,从而不耗费多余的等待时间,虽然这样也可以,但一般在这种形势下,推荐使用dispatch_group_notify追加结束处理到main_get_main_queue,因为这样可以简化代码。
(6)diapatch_barrier_async
在访问数据库文件时,如前所述,使用serial dispatch queue可避免数据竞争问题。写入处理确实不可与其他的写入处理以及包含读取处理的其它某些处理并行执行。但是如果读取处理只是与读取处理并行执行,那么多个并行执行就不会发生问题。dispatch_barrier_async函数的作用与barrier的意思相同,在进程管理中起到一个栅栏的作用,它等待所有位于barrier函数之前的操作执行完毕后执行,并且在barrier函数执行之后,barrier函数之后的操作才会得到执行,该函数需要同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用。
1.实现高效率的数据库访问和文件访问。
2.避免数据竞争。
代码示例:
dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.ForBarrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"dispatch-1");
});
dispatch_async(queue, ^{
NSLog(@"dispatch-2");
});
dispatch_barrier_async(queue, ^{
sleep();
NSLog(@"dispatch-barrier");
});
dispatch_async(queue, ^{
NSLog(@"dispatch-3");
});
dispatch_async(queue, ^{
NSLog(@"dispatch-4");
});
-- ::12.270 GCD-test[:] dispatch-
-- ::12.270 GCD-test[:] dispatch-
-- ::17.275 GCD-test[:] dispatch-barrier
-- ::17.276 GCD-test[:] dispatch-
-- ::17.276 GCD-test[:] dispatch-
(7)dispatch_apply
会等待全部处理执行结束,可用于对nsarray对象的所有元素分别进行处理时,不必一个一个便携for循环部分。
(8)dispatch_sync
同步的将Block追加到指定的Dispatch Queue中。在追加的Block结束之前,Dispatch Queue函数会一直等待。
死锁:dispatch_sync的当前执行队列与提交block执行的目标队列相同时造成死锁。dispatch_sync会堵塞当前线程,等待block执行完毕后返回,但是当前的block加入到了当前的队列等待dispatch_sync的执行完毕,互相依赖,产生死锁。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue, ^{
NSLog(@"hello");
});
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篇
什么是GCD GCD是苹果对多线程编程做的一套新的抽象基于C语言层的API,结合Block简化了多线程的操作,使得我们对线程操作能够更加的安全高效. 在GCD出现之前Cocoa框架提供了NSObjec ...
- iOS 多线程GCD简介
一.简介 1.1 GCD (Grand Central Dispatch )是Apple开发的一个多核编程的解决方法. Grand 含义是“伟大的.宏大的”,Central含义“中央的”,Dispat ...
随机推荐
- angular表格分页
<!doctype html> <html lang="en" > <head> <meta charset="UTF-8&qu ...
- UE4里的渲染线程
记的上次看过UniRx里的源代码,说是参考微软的响应式编程框架,响应式编程里的一些理论不细说,只单说UniRx里的事件流里的事件压入与执行,与UE4的渲染线程设计有很多相同之处,如果有了解响应式编程相 ...
- gcc 简单编译流程
注意:GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,可在编译时加上-static选项,强制使用静态链接库. gcc -static 此选项将禁止使用动态库,所以,编 ...
- TimSort in Java 8
在项目中使用了Collections.sort(list, comparator)对集合进行了排序,偶然间遇到异常IllegalArgumentException: "Comparison ...
- C++编程练习(4)----“实现简单的栈的链式存储结构“
如果栈的使用过程中元素数目变化不可预测,有时很小,有时很大,则最好使用链栈:反之,如果它的变化在可控范围内,使用顺序栈会好一些. 简单的栈的链式存储结构代码如下: /*LinkStack.h*/ #i ...
- 你需要了解的 Core Spotlight
你需要了解的 Core Spotlight -- 了解Spotlight Core Spotlight是iOS 9中苹果推出了新的Search API,可以直接搜App内的内容(in-App Sear ...
- ADODB——RecordSet对象
转自网友,看着挺全就转了,供大家学习研究. Recordset 对象的属性 1.CursorType 属性 AdOpenForwardOnly: 仅向前游标,默认值.除了只能在记录中向前滚动外,与静态 ...
- 理解redis高可用方案
*:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...
- JavaScript 深浅拷贝
JavaScript有五种基本数据类型(Undefined, null, Boolean, String, Number),还有一种复杂的数据类型,就是对象. Undefined 其实是已声明但没有赋 ...
- mysql常用语法
创建表 create table <表名>( <字段名> 类型(长度) not null primary key auto_increment, **主键 name char ...