gcd曲线可以说明啥
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gcd曲线可以说明啥相关的知识,希望对你有一定的参考价值。
什么是 GCD 呢?Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。在 Mac OS X 10.6 雪豹中首次推出,也可在 ios 4 及以上版本使用。
为什么要用 GCD 呢?
因为 GCD 有很多好处啊,具体如下:
GCD 可用于多核的并行运算
GCD 会自动利用更多的 CPU 内核(比如双核、四核)
GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
既然 GCD 有这么多的好处,那么下面我们就来系统的学习一下 GCD 的使用方法。
2. GCD 任务和队列
学习 GCD 之前,先来了解 GCD 中两个核心概念:任务和队列。
任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。
同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。
只能在当前线程中执行任务,不具备开启新线程的能力。
异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
可以在新的线程中执行任务,具备开启新线程的能力。
举个简单例子:你要打电话给小明和小白。
同步执行就是,你打电话给小明的时候,不能同时打给小白,等到给小明打完了,才能打给小白(等待任务执行结束)。而且只能用当前的电话(不具备开启新线程的能力)。
而异步执行就是,你打电话给小明的时候,不等和小明通话结束,还能直接给小白打电话,不用等着和小明通话结束再打(不用等待任务执行结束)。除了当前电话,你还可以使用其他所能使用的电话(具备开启新线程的能力)。
注意: 异步执行(async) 虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关(下面会讲)。
队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。队列的结构可参考下图:

在 GCD 中有两种队列:串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。
串行队列(Serial Dispatch Queue):
每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
并发队列(Concurrent Dispatch Queue):
可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)
注意:并发队列 的并发功能只有在异步(dispatch_async)函数下才有效
两者具体区别如下两图所示。
3. GCD 的使用步骤
GCD 的使用步骤其实很简单,只有两步。
创建一个队列(串行队列或并发队列)
将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)
下边来看看 队列的创建方法/获取方法,以及 任务的创建方法。
3.1 队列的创建方法/获取方法
可以使用dispatch_queue_create来创建队列,需要传入两个参数,第一个参数表示队列的唯一标识符,用于 DEBUG,可为空,Dispatch Queue 的名称推荐使用应用程序 ID 这种逆序全程域名;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL 表示串行队列,DISPATCH_QUEUE_CONCURRENT 表示并发队列。
// 串行队列的创建方法dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_SERIAL);// 并发队列的创建方法dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_CONCURRENT);
// 主队列的获取方法dispatch_queue_t queue = dispatch_get_main_queue();
// 全局并发队列的获取方法dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
3.2 任务的创建方法
GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async。
// 同步执行任务创建方法dispatch_sync(queue, ^ // 这里放同步执行任务代码);// 异步执行任务创建方法dispatch_async(queue, ^ // 这里放异步执行任务代码);
虽然使用 GCD 只需两步,但是既然我们有两种队列(串行队列/并发队列),两种任务执行方式(同步执行/异步执行),那么我们就有了四种不同的组合方式。这四种不同的组合方式是:
1.同步执行 + 并发队列
2.异步执行 + 并发队列
3.同步执行 + 串行队列
4.异步执行 + 串行队列
实际上,刚才还说了两种特殊队列:全局并发队列、主队列。全局并发队列可以作为普通并发队列来使用。但是主队列因为有点特殊,所以我们就又多了两种组合方式。这样就有六种不同的组合方式了。
5.同步执行 + 主队列
6.异步执行 + 主队列
那么这几种不同组合方式各有什么区别呢,这里为了方便,先上结果,再来讲解。你可以直接查看表格结果,然后跳过 4. GCD 的基本使用 。

下边我们来分别讲讲这几种不同的组合方式的使用方法。
4. GCD 的基本使用
先来讲讲并发队列的两种执行方式。
4.1 同步执行 + 并发队列
/ 同步执行 + 并发队列 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。 /- (void)syncConcurrent NSLog(@”currentThread—%@”,[NSThread currentThread]); // 打印当前线程 NSLog(@”syncConcurrent—begin”); dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^ // 追加任务1 for (int i = 0; i
输出结果:
2018-02-23 20:34:55.095932+0800 YSC-GCD-demo[19892:4996930] currentThread—number = 1, name = main
2018-02-23 20:34:55.096086+0800 YSC-GCD-demo[19892:4996930] syncConcurrent—begin
2018-02-23 20:34:57.097589+0800 YSC-GCD-demo[19892:4996930] 1—number = 1, name = main
2018-02-23 20:34:59.099100+0800 YSC-GCD-demo[19892:4996930] 1—number = 1, name = main
2018-02-23 20:35:01.099843+0800 YSC-GCD-demo[19892:4996930] 2—number = 1, name = main
2018-02-23 20:35:03.101171+0800 YSC-GCD-demo[19892:4996930] 2—number = 1, name = main
2018-02-23 20:35:05.101750+0800 YSC-GCD-demo[19892:4996930] 3—number = 1, name = main
2018-02-23 20:35:07.102414+0800 YSC-GCD-demo[19892:4996930] 3—number = 1, name = main
2018-02-23 20:35:07.102575+0800 YSC-GCD-demo[19892:4996930] syncConcurrent—end
从同步执行 + 并发队列中可看到:
所有任务都是在当前线程(主线程)中执行的,没有开启新的线程(同步执行不具备开启新线程的能力)。
所有任务都在打印的syncConcurrent—begin和syncConcurrent—end之间执行的(同步任务需要等待队列的任务执行结束)。
任务按顺序执行的。按顺序执行的原因:虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建新线程,只有当前线程这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。
4.2 异步执行 + 并发队列
可以开启多个线程,任务交替(同时)执行。
/ 异步执行 + 并发队列 特点:可以开启多个线程,任务交替(同时)执行。 /- (void)asyncConcurrent NSLog(@”currentThread—%@”,[NSThread currentThread]); // 打印当前线程 NSLog(@”asyncConcurrent—begin”); dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^ // 追加任务1 for (int i = 0; i
输出结果:
2018-02-23 20:36:41.769269+0800 YSC-GCD-demo[19929:5005237] currentThread—number = 1, name = main
2018-02-23 20:36:41.769496+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent—begin
2018-02-23 20:36:41.769725+0800 YSC-GCD-demo[19929:5005237] asyncConcurrent—end
2018-02-23 20:36:43.774442+0800 YSC-GCD-demo[19929:5005566] 2—number = 5, name = (null)
2018-02-23 20:36:43.774440+0800 YSC-GCD-demo[19929:5005567] 3—number = 4, name = (null)
2018-02-23 20:36:43.774440+0800 YSC-GCD-demo[19929:5005565] 1—number = 3, name = (null)
2018-02-23 20:36:45.779286+0800 YSC-GCD-demo[19929:5005567] 3—number = 4, name = (null)
2018-02-23 20:36:45.779302+0800 YSC-GCD-demo[19929:5005565] 1—number = 3, name = (null)
2018-02-23 20:36:45.779286+0800 YSC-GCD-demo[19929:5005566] 2—number = 5, name = (null)
在异步执行 + 并发队列中可以看出:
除了当前线程(主线程),系统又开启了3个线程,并且任务是交替/同时执行的。(异步执行具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。
所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才执行的。说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务(异步执行不做等待,可以继续执行任务)。
接下来再来讲讲串行队列的两种执行方式。
4.3 同步执行 + 串行队列
/ 同步执行 + 串行队列 特点:不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。 /- (void)syncSerial NSLog(@”currentThread—%@”,[NSThread currentThread]); // 打印当前线程 NSLog(@”syncSerial—begin”); dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^ // 追加任务1 for (int i = 0; i
输出结果:
2018-02-23 20:39:37.876811+0800 YSC-GCD-demo[19975:5017162] currentThread—number = 1, name = main
2018-02-23 20:39:37.876998+0800 YSC-GCD-demo[19975:5017162] syncSerial—begin
2018-02-23 20:39:39.878316+0800 YSC-GCD-demo[19975:5017162] 1—number = 1, name = main
2018-02-23 20:39:41.879829+0800 YSC-GCD-demo[19975:5017162] 1—number = 1, name = main
2018-02-23 20:39:43.880660+0800 YSC-GCD-demo[19975:5017162] 2—number = 1, name = main
2018-02-23 20:39:45.881265+0800 YSC-GCD-demo[19975:5017162] 2—number = 1, name = main
2018-02-23 20:39:47.882257+0800 YSC-GCD-demo[19975:5017162] 3—number = 1, name = main
2018-02-23 20:39:49.883008+0800 YSC-GCD-demo[19975:5017162] 3—number = 1, name = main
2018-02-23 20:39:49.883253+0800 YSC-GCD-demo[19975:5017162] syncSerial—end
在同步执行 + 串行队列可以看到:
所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(同步执行不具备开启新线程的能力)。
所有任务都在打印的syncConcurrent—begin和syncConcurrent—end之间执行(同步任务需要等待队列的任务执行结束)。
任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。
4.4 异步执行 + 串行队列
/ 异步执行 + 串行队列 特点:会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。 /- (void)asyncSerial NSLog(@”currentThread—%@”,[NSThread currentThread]); // 打印当前线程 NSLog(@”asyncSerial—begin”); dispatch_queue_t queue = dispatch_queue_create(“net.bujige.testQueue”, DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^ // 追加任务1 for (int i = 0; i
输出结果:
2018-02-23 20:41:17.029999+0800 YSC-GCD-demo[20008:5024757] currentThread—number = 1, name = main
2018-02-23 20:41:17.030212+0800 YSC-GCD-demo[20008:5024757] asyncSerial—begin
2018-02-23 20:41:17.030364+0800 YSC-GCD-demo[20008:5024757] asyncSerial—end
2018-02-23 20:41:19.035379+0800 YSC-GCD-demo[20008:5024950] 1—number = 3, name = (null)
2018-02-23 20:41:21.037140+0800 YSC-GCD-demo[20008:5024950] 1—number = 3, name = (null)
2018-02-23 20:41:23.042220+0800 YSC-GCD-demo[20008:5024950] 2—number = 3, name = (null)
2018-02-23 20:41:25.042971+0800 YSC-GCD-demo[20008:5024950] 2—number = 3, name = (null)
2018-02-23 20:41:27.047690+0800 YSC-GCD-demo[20008:5024950] 3—number = 3, name = (null)
2018-02-23 20:41:29.052327+0800 YSC-GCD-demo[20008:5024950] 3—number = 3, name = (null)
在异步执行 + 串行队列可以看到:
开启了一条新线程(异步执行具备开启新线程的能力,串行队列只开启一个线程)。
所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。
任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。
下边讲讲刚才我们提到过的特殊队列:主队列。
我们再来看看主队列的两种组合方式。
4.5 同步执行 + 主队列
同步执行 + 主队列在不同线程中调用结果也是不一样,在主线程中调用会出现死锁,而在其他线程中则不会。
4.5.1 在主线程中调用同步执行 + 主队列
/ 同步执行 + 主队列 特点(主线程调用):互等卡主不执行。 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。 /- (void)syncMain NSLog(@”currentThread—%@”,[NSThread currentThread]); // 打印当前线程 NSLog(@”syncMain—begin”); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^ // 追加任务1 for (int i = 0; i
输出结果:
2018-02-23 20:42:36.842892+0800 YSC-GCD-demo[20041:5030982] currentThread—number = 1, name = main
2018-02-23 20:42:36.843050+0800 YSC-GCD-demo[20041:5030982] syncMain—begin
(lldb)
在同步执行 + 主队列可以惊奇的发现:
这是因为我们在主线程中执行syncMain方法,相当于把syncMain任务放到了主线程的队列中。而同步执行会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务1追加到主队列中,任务1就在等待主线程处理完syncMain任务。而syncMain任务需要等待任务1执行完毕,才能接着执行。
那么,现在的情况就是syncMain任务和任务1都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且syncMain—end也没有打印。 参考技术A "GCD曲线"(也称为"欧几里德算法曲线")是一种数学曲线,它通过对两个正整数的欧几里德算法运算进行可视化得到。这种曲线的横坐标表示两个数中较大的那个,纵坐标表示较小的那个,曲线上的每个点表示欧几里德算法的一次迭代。
欧几里德算法是求两个正整数的最大公约数(GCD)的经典方法。GCD曲线可以用来帮助人们直观地理解欧几里德算法的运作方式和运算效率。在GCD曲线上,可以看到欧几里德算法运算的每一步,以及在每一步中剩余的两个数的关系。此外,通过GCD曲线,还可以发现GCD算法的重要性质,即GCD(a,b)= GCD(b,a mod b),其中“mod”表示取模运算。
总之,GCD曲线是一种可视化工具,可以帮助人们更好地理解欧几里德算法以及它在计算机科学和数学中的应用。 参考技术B 您好!GCD曲线是一种用于描述密码学中椭圆曲线加密算法的数学工具。它可以用来说明椭圆曲线加密算法中的密钥交换过程以及数字签名的过程。
在密钥交换过程中,GCD曲线可以用来说明如何利用椭圆曲线上的点的加法运算来生成共享密钥。具体来说,Alice和Bob可以先各自生成一对公私钥,然后通过交换各自的公钥来生成共享密钥。在这个过程中,GCD曲线可以用来说明如何利用椭圆曲线上的点的加法运算来计算共享密钥。
在数字签名的过程中,GCD曲线可以用来说明如何利用椭圆曲线上的点的乘法运算来生成数字签名。具体来说,签名者可以先将待签名的消息转化为一个椭圆曲线上的点,然后利用自己的私钥对这个点进行乘法运算,得到一个数字签名。在验证签名的过程中,验证者可以利用签名者的公钥和椭圆曲线上的点的加法运算来验证数字签名的有效性。
总之,GCD曲线是椭圆曲线加密算法中非常重要的数学工具,它可以用来说明密钥交换和数字签名的过程。 参考技术C gcd曲线是一种表示两个数之间最大公约数的图形方法。它可以说明两个数之间的最大公约数的变化情况,从而帮助人们更好地理解数学中最大公约数的概念和性质。
具体来说,gcd曲线可以说明以下几点:
两个数越接近,它们的最大公约数就越大。这是因为当两个数接近时,它们之间的公因数就越多,所以最大公约数也就越大。
当两个数互质时,它们的gcd曲线为1。这是因为两个互质的数没有公因数,因此它们的最大公约数只能是1。
当两个数相等时,它们的gcd曲线为该数本身。这是因为一个数和自己的最大公约数就是它本身。
总之,gcd曲线为我们提供了一种直观的方式来观察两个数之间最大公约数的变化情况,从而深入理解数学中最大公约数的概念和性质。 参考技术D gcd曲线指的是两个数列的最大公约数随着下标的增加而变化的图像。它可以用来研究两个数列之间的关系,通常应用于数字论和密码学等领域。
具体来说,当两个数列的最大公约数不断变化时,可以将每一步的最大公约数绘制在坐标系中,最后连接起来就形成了gcd曲线。通过这条曲线的形态和特征,我们可以得到以下几点信息:
1. 两个数列的周期性:如果gcd曲线呈现出明显的周期性变化,那么这两个数列也有相应的周期性,这对于研究密码学中的伪随机数生成器非常有用。
2. 两个数列的关联程度:gcd曲线的波动范围越小,则说明这两个数列之间的关联越强;反之亦然。这对于研究两个数列之间的相关性非常有用。
3. 寻找最大公约数:如果可以找到gcd曲线上的某个峰值或谷值,那么它所对应的坐标就是这两个数列的最大公约数。这对于加密算法中的公钥生成十分重要。
GCD 使用说明
GCD提供的一些操作队列的方法
名称 | 说明 |
dispatch_set_target_queue | 将多个队列添加到目标队列中 |
dispatch_group | 将多个队列放入组中,监听所有任务完成状 |
dispatch_suspend | 队列挂起 |
dispatch_resume | 队列恢复 |
dispatch_sync | 线程同步执行 |
dispatch_async | 线程异步执行 |
dispatch_after | 延迟执行态 |
dispatch_barrier_async | 并发队列中,完成在它之前提交到队列中的任务后打断后面任务 |
dispatch_apply | 实现无序查找 |
- dispatch_set_target_queue
系统的Global Queue是可以指定优先级的,那我们如何给自己创建的队列执行优先级呢?
这里我们就可以用到dispatch_set_target_queue这个方法:
dispatch_queue_t serialDiapatchQueue=dispatch_queue_create("com.test.queue", NULL); dispatch_queue_t dispatchgetglobalqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //将serialDiapatchQueue放到全局队列中作为子队列,这样优先级就是使用默认 dispatch_set_target_queue(serialDiapatchQueue, dispatchgetglobalqueue); dispatch_async(serialDiapatchQueue, ^{ NSLog(@"我优先级低,先让让"); }); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"我优先级高,我先block"); });
我把自己创建的队列塞到了系统提供的global_queue队列中,我们可以理解为:我们自己创建的queue其实是位于global_queue中执行,
所以改变global_queue的优先级,也就改变了我们自己所创建的queue的优先级。所以我们常用这种方式来管理子队列。
(一),使用dispatch_set_target_queue更改Dispatch Queue的执行优先级
dispatch_queue_create函数生成的DisPatch Queue不管是Serial DisPatch Queue还是Concurrent Dispatch Queue,执行的优先级都与默认优先级的Global Dispatch queue相同,如果需要变更生成的Dispatch Queue的执行优先级则需要使用dispatch_set_target_queue函数
- (void)testTeagerQueue1 { dispatch_queue_t serialQueue = dispatch_queue_create("com.oukavip.www",NULL); dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0); // 第一个参数为要设置优先级的queue,第二个参数是参照物,既将第一个queue的优先级和第二个queue的优先级设置一样。 dispatch_set_target_queue(serialQueue, globalQueue); }
(二),使用dispatch_set_target_queue修改用户队列的目标队列,使多个serial queue在目标queue上一次只有一个执行
首先,我们需要阐述一下生成多个Serial DisPatch Queue时的注意事项
Serial DisPatch Queue是一个串行队列,只能同时执行1个追加处理(即任务),当用Dispatch_queue_create函数生成多个Serial DisPatch Queue时,每个Serial DisPatch Queue均获得一个线程,即多个Serial DisPatch Queue可并发执行,同时处理添加到各个Serial DisPatch Queue中的任务,但要注意如果过多地使用多线程,就会消耗大量内存,引起大量的上下文切换,大幅度降低系统的响应性能,所以我们只在为了避免多个线程更新相同资源导致数据竞争时,使用Serial DisPatch Queue
第一种情况:使用dispatch_set_target_queue(Dispatch Queue1, Dispatch Queue2)实现队列的动态调度管理
- (void)testTargetQueue2 { //创建一个串行队列queue1 dispatch_queue_t queue1 = dispatch_queue_create("test.1", DISPATCH_QUEUE_SERIAL); //创建一个串行队列queue2 dispatch_queue_t queue2 = dispatch_queue_create("test.2", DISPATCH_QUEUE_SERIAL); //使用dispatch_set_target_queue()实现队列的动态调度管理 dispatch_set_target_queue(queue1, queue2); /* <*>dispatch_set_target_queue(Dispatch Queue1, Dispatch Queue2); 那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行: <*>dispatch_suspend(dispatchA); 这时则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。 这里只简单举个例子,说明dispatch队列运行的灵活性,在实际应用中你会逐步发掘出它的潜力。 dispatch队列不支持cancel(取消),没有实现dispatch_cancel()函数,不像NSOperationQueue,不得不说这是个小小的 */ dispatch_async(queue1, ^{ for (NSInteger i = 0; i < 10; i++) { NSLog(@"queue1:%@, %ld", [NSThread currentThread], i); [NSThread sleepForTimeInterval:0.5]; if (i == 5) { dispatch_suspend(queue2); } } }); dispatch_async(queue1, ^{ for (NSInteger i = 0; i < 100; i++) { NSLog(@"queue1:%@, %ld", [NSThread currentThread], i); } }); dispatch_async(queue2, ^{ for (NSInteger i = 0; i < 100; i++) { NSLog(@"queue2:%@, %ld", [NSThread currentThread], i); } }); }
第二种情况:使用dispatch_set_target_queue将多个串行的queue指定到了同一目标,那么着多个串行queue在目标queue上就是同步执行的,不再是并行执行。
- (void)testTargetQueue { //1.创建目标队列 dispatch_queue_t targetQueue = dispatch_queue_create("test.target.queue", DISPATCH_QUEUE_SERIAL); //2.创建3个串行队列 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); //3.将3个串行队列分别添加到目标队列 dispatch_set_target_queue(queue1, targetQueue); dispatch_set_target_queue(queue2, targetQueue); dispatch_set_target_queue(queue3, targetQueue); dispatch_async(queue1, ^{ NSLog(@"1 in"); [NSThread sleepForTimeInterval:3.f]; NSLog(@"1 out"); }); dispatch_async(queue2, ^{ NSLog(@"2 in"); [NSThread sleepForTimeInterval:2.f]; NSLog(@"2 out"); }); dispatch_async(queue3, ^{ NSLog(@"3 in"); [NSThread sleepForTimeInterval:1.f]; NSLog(@"3 out"); }); }
- dispatch_after
这个是最常用的,用来延迟执行的GCD方法,因为在主线程中我们不能用sleep来延迟方法的调用,所以用它是最合适的,我们做一个简单的例子:
NSLog(@"小破孩-波波1"); double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ NSLog(@"小破孩-波波2"); });
输出的结果:
2016-03-07 11:25:06.019 GCD[2443:95722] 小破孩-波波1 2016-03-07 11:25:08.019 GCD[2443:95722] 小破孩-波波2
我们看到他就是在主线程,就是刚好延迟了2秒,当然,我说这个2秒并不是绝对的,为什么这么说?还记得我之前在介绍dispatch_async这个特性的时候提到的吗?他的block中方法的执行会放在主线程runloop之后,所以,如果此时runloop周期较长的时候,可能会有一些时差产生。
- dispatch_group
当我们需要监听一个并发队列中,所有任务都完成了,就可以用到这个group,因为并发队列你并不知道哪一个是最后执行的,所以以单独一个任务是无法监听到这个点的,如果把这些单任务都放到同一个group,那么,我们就能通过dispatch_group_notify方法知道什么时候这些任务全部执行完成了。
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group=dispatch_group_create(); dispatch_group_async(group, queue, ^{NSLog(@"0");}); dispatch_group_async(group, queue, ^{NSLog(@"1");}); dispatch_group_async(group, queue, ^{NSLog(@"2");}); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"down"); });
在例子中,我把3个log分别放在并发队列中,通过把这个并发队列任务统一加入group中,group每次runloop的时候都会调用一个方法dispatch_group_wait(group, DISPATCH_TIME_NOW),用来检查group中的任务是否已经完成,如果已经完成了,那么会执行dispatch_group_notify的block,输出’down’看一下运行结果:
1 2016-03-07 14:21:58.647 GCD[9424:156388] 2 2 2016-03-07 14:21:58.647 GCD[9424:156382] 0 3 2016-03-07 14:21:58.647 GCD[9424:156385] 1 4 2016-03-07 14:21:58.650 GCD[9424:156324] down
- dispatch_barrier_async
此方法的作用是在并发队列中,完成在它之前提交到队列中的任务后打断,单独执行其block,并在执行完成之后才能继续执行在他之后提交到队列中的任务:
dispatch_queue_t concurrentDiapatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"0");}); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"1");}); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"2");}); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"3");}); dispatch_barrier_async(concurrentDiapatchQueue, ^{ sleep(1); NSLog(@"4"); }); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"5");}); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"6");}); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"7");}); dispatch_async(concurrentDiapatchQueue, ^{NSLog(@"8");});
输出的结果为:
2016-03-07 14:45:32.410 GCD[10079:169655] 1 2016-03-07 14:45:32.410 GCD[10079:169658] 2 2016-03-07 14:45:32.410 GCD[10079:169656] 0 2016-03-07 14:45:32.410 GCD[10079:169661] 3 2016-03-07 14:45:33.414 GCD[10079:169661] 4 2016-03-07 14:45:33.415 GCD[10079:169661] 5 2016-03-07 14:45:33.415 GCD[10079:169658] 6 2016-03-07 14:45:33.415 GCD[10079:169655] 8 2016-03-07 14:45:33.415 GCD[10079:169662] 7
4之后的任务在我线程sleep之后才执行,这其实就起到了一个线程锁的作用,在多个线程同时操作一个对象的时候,读可以放在并发进行,当写的时候,我们就可以用dispatch_barrier_async方法,效果杠杠的。
- dispatch_sync
dispatch_sync 会在当前线程执行队列,并且阻塞当前线程中之后运行的代码,所以,同步线程非常有可能导致死锁现象,我们这边就举一个死锁的例子,直接在主线程调用以下代码:
dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"有没有同步主线程?"); });
*根据FIFO(先进先出)的原则,block里面的代码应该在主线程此次runloop后执行,但是由于他是同步队列,所有他之后的代码会等待其执行完成后才能继续执行,2者相互等待,所以就出现了死锁。
我们再举一个比较特殊的例子:
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_sync(queue, ^{sleep(1);NSLog(@"1");}); dispatch_sync(queue, ^{sleep(1);NSLog(@"2");}); dispatch_sync(queue, ^{sleep(1);NSLog(@"3");}); NSLog(@"4");
其打印结果为:
1 2016-03-07 17:15:48.124 GCD[14198:272683] 1 2 2016-03-07 17:15:49.125 GCD[14198:272683] 2 3 2016-03-07 17:15:50.126 GCD[14198:272683] 3 4 2016-03-07 17:15:50.126 GCD[14198:272683] 4
从线程编号中我们发现,同步方法没有去开新的线程,而是在当前线程中执行队列,会有人问,上文说dispatch_get_global_queue不是并发队列,并发队列不是应该会在开启多个线程吗?这个前提是用异步方法。GCD其实是弱化了线程的管理,强化了队列管理,这使我们理解变得比较形象
- dispatch_apply
这个方法用于无序查找,在一个数组中,我们能开启多个线程来查找所需要的值,我这边也举个例子:
NSArray *array=[[NSArray alloc]initWithObjects:@"0",@"1",@"2",@"3",@"4",@"5",@"6", nil]; dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply([array count], queue, ^(size_t index) { NSLog(@"%zu=%@",index,[array objectAtIndex:index]); }); NSLog(@"阻塞");
输出结果
2016-03-07 17:36:50.726 GCD[14318:291701] 1=1 2016-03-07 17:36:50.726 GCD[14318:291705] 0=0 2016-03-07 17:36:50.726 GCD[14318:291783] 3=3 2016-03-07 17:36:50.726 GCD[14318:291782] 2=2 2016-03-07 17:36:50.726 GCD[14318:291784] 5=5 2016-03-07 17:36:50.726 GCD[14318:291627] 4=4 2016-03-07 17:36:50.726 GCD[14318:291785] 6=6 2016-03-07 17:36:50.727 GCD[14318:291627] 阻塞
通过输出log,我们发现这个方法虽然会开启多个线程来遍历这个数组,但是在遍历完成之前会阻塞主线程。
- dispatch_suspend & dispatch_resume
队列挂起和恢复,这个没什么好说的,直接上代码:
dispatch_queue_t concurrentDiapatchQueue=dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentDiapatchQueue, ^{ for (int i=0; i<100; i++) { NSLog(@"%i",i); if (i==50) { NSLog(@"-----------------------------------"); dispatch_suspend(concurrentDiapatchQueue); sleep(3); dispatch_async(dispatch_get_main_queue(), ^{ dispatch_resume(concurrentDiapatchQueue); }); } } });
我们甚至可以在不同的线程对这个队列进行挂起和恢复,因为GCD是对队列的管理。
- Semaphore
我们可以通过设置信号量的大小,来解决并发过多导致资源吃紧的情况,以单核CPU做并发为例,一个CPU永远只能干一件事情,那如何同时处理多个事件呢,聪明的内核工程师让CPU干第一件事情,一定时间后停下来,存取进度,干第二件事情以此类推,所以如果开启非常多的线程,单核CPU会变得非常吃力,即使多核CPU,核心数也是有限的,所以合理分配线程,变得至关重要,那么如何发挥多核CPU的性能呢?如果让一个核心模拟传很多线程,经常干一半放下干另一件事情,那效率也会变低,所以我们要合理安排,将单一任务或者一组相关任务并发至全局队列中运算或者将多个不相关的任务或者关联不紧密的任务并发至用户队列中运算,所以用好信号量,合理分配CPU资源,程序也能得到优化,当日常使用中,信号量也许我们只起到了一个计数的作用,真的有点大材小用。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);//为了让一次输出10个,初始信号量为10 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); for (int i = 0; i <100; i++) { dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//每进来1次,信号量-1;进来10次后就一直hold住,直到信号量大于0; dispatch_async(queue, ^{ NSLog(@"%i",i); sleep(2); dispatch_semaphore_signal(semaphore);//由于这里只是log,所以处理速度非常快,我就模拟2秒后信号量+1; }); }
- dispatch_once
这个函数一般是用来做一个单例,也是非常常用的,下面是一个简单用例:
static SingletonTimer * instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[SingletonTimer alloc] init]; }); return instance;
以上是关于gcd曲线可以说明啥的主要内容,如果未能解决你的问题,请参考以下文章