原文链接:https://www.jianshu.com/p/2d57c72016c6

本文用来介绍 iOS 多线程中 GCD 的相关知识以及使用方法。通过本文,您将了解到:

1. GCD 简介

2. GCD 任务和队列

3. GCD 的使用步骤

4. GCD 的基本使用(6种不同组合区别)

1. 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)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力。

同步执行(sync):

同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。

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

异步执行(async):

异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。

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

举个简单例子:你要打电话给小明和小白。

同步执行就是,你打电话给小明的时候,不能同时打给小白,等到给小明打完了,才能打给小白(等待任务执行结束)。而且只能用当前的电话(不具备开启新线程的能力)。

而异步执行就是,你打电话给小明的时候,不等和小明通话结束,还能直接给小白打电话,不用等着和小明通话结束再打(不用等待任务执行结束)。除了当前电话,你还可以使用其他所能使用的电话(具备开启新线程的能力)。

注意:异步执行(async)虽然具有开启新线程的能力,但是并不一定开启新线程。这跟任务所指定的队列类型有关(下面会讲)。

队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。队列的结构可参考下图:

Serial串行队列.png

在 GCD 中有两种队列:串行队列和并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是:执行顺序不同,以及开启线程数不同。

串行队列(Serial Dispatch Queue):

每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)

并发队列(Concurrent Dispatch Queue):

可以让多个任务并发(同时)执行。(可以开启多个线程,并且同时执行任务)注意:并发队列的并发功能只有在异步(dispatch_async)函数下才有效

两者具体区别如下两图所示。

串行队列.png

并发队列.png

3. GCD 的使用步骤

GCD 的使用步骤其实很简单,只有两步。

1.创建一个队列(串行队列或并发队列)

2.将任务追加到任务的等待队列中,然后系统就会根据任务类型执行任务(同步执行或异步执行)

下边来看看队列的创建方法/获取方法,以及任务的创建方法。

3.1 队列的创建方法/获取方法

可以使用dispatch_queue_create来创建队列,需要传入两个参数,第一个参数表示队列的唯一标识符,用于 DEBUG,可为空,Dispatch Queue 的名称推荐使用应用程序 ID 这种逆序全程域名;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。// 串行队列的创建方法dispatch_queue_tqueue= dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

// 并发队列的创建方法dispatch_queue_tqueue= dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

对于串行队列,GCD 提供了的一种特殊的串行队列:主队列(Main Dispatch Queue)。

a.所有放在主队列中的任务,都会放到主线程中执行。

b.可使用dispatch_get_main_queue()获得主队列。// 主队列的获取方法

dispatch_queue_t queue= dispatch_get_main_queue();

对于并发队列,GCD 默认提供了全局并发队列(Global Dispatch Queue)。

a.可以使用dispatch_get_global_queue来获取。需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。// 全局并发队列的获取方法

dispatch_queue_tqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

3.2 任务的创建方法

GCD 提供了同步执行任务的创建方法dispatch_sync和异步执行任务创建方法dispatch_async。// 同步执行任务创建方法

dispatch_sync(queue, ^{// 这里放同步执行任务代码});

// 异步执行任务创建方法

dispatch_async(queue, ^{// 这里放异步执行任务代码});

虽然使用 GCD 只需两步,但是既然我们有两种队列(串行队列/并发队列),两种任务执行方式(同步执行/异步执行),那么我们就有了四种不同的组合方式。这四种不同的组合方式是:同步执行 + 并发队列

异步执行 + 并发队列

同步执行 + 串行队列

异步执行 + 串行队列

实际上,刚才还说了两种特殊队列:全局并发队列、主队列。全局并发队列可以作为普通并发队列来使用。但是主队列因为有点特殊,所以我们就又多了两种组合方式。这样就有六种不同的组合方式了。同步执行 + 主队列

异步执行 + 主队列

那么这几种不同组合方式各有什么区别呢,这里为了方便,先上结果,再来讲解。你可以直接查看表格结果,然后跳过4. GCD的基本使用。

下边我们来分别讲讲这几种不同的组合方式的使用方法。

4. GCD 的基本使用

先来讲讲并发队列的两种执行方式。

4.1 同步执行 + 并发队列

在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。/**

* 同步执行 + 并发队列

* 特点:在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。

*/

- (void)syncConcurrent {

NSLog(@"currentThread---%@",[NSThreadcurrentThread]);

// 打印当前线程NSLog(@"syncConcurrent---begin");

dispatch_queue_tqueue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_sync(queue, ^{

// 追加任务1

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_sync(queue, ^{

// 追加任务2

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_sync(queue, ^{

// 追加任务3

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

NSLog(@"syncConcurrent---end");}输出结果:

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

从同步执行 + 并发队列中可看到:

a.所有任务都是在当前线程(主线程)中执行的,没有开启新的线程(同步执行不具备开启新线程的能力)。

b.所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行的(同步任务需要等待队列的任务执行结束)。

任务按顺序执行的。按顺序执行的原因:虽然并发队列可以开启多个线程,并且同时执行多个任务。但是因为本身不能创建新线程,只有当前线程这一个线程(同步任务不具备开启新线程的能力),所以也就不存在并发。而且当前线程只有等待当前队列中正在执行的任务执行完毕之后,才能继续接着执行下面的操作(同步任务需要等待队列的任务执行结束)。所以任务只能一个接一个按顺序执行,不能同时被执行。

4.2 异步执行 + 并发队列

可以开启多个线程,任务交替(同时)执行。/**

* 异步执行 + 并发队列

* 特点:可以开启多个线程,任务交替(同时)执行。

*/- (void)asyncConcurrent {

NSLog(@"currentThread---%@",[NSThreadcurrentThread]);

// 打印当前线程NSLog(@"asyncConcurrent---begin");

dispatch_queue_tqueue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

// 追加任务1for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程}

});

dispatch_async(queue, ^{

// 追加任务2

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_async(queue, ^{

// 追加任务3

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

NSLog(@"asyncConcurrent---end");}输出结果:

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)}

在异步执行 + 并发队列中可以看出:

a.除了当前线程(主线程),系统又开启了3个线程,并且任务是交替/同时执行的。(异步执行具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。

b.所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才执行的。说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务(异步执行不做等待,可以继续执行任务)。

接下来再来讲讲串行队列的两种执行方式。

4.3 同步执行 + 串行队列

不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。/**

* 同步执行 + 串行队列

* 特点:不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。

*/

- (void)syncSerial {

NSLog(@"currentThread---%@",[NSThreadcurrentThread]);// 打印当前线程

NSLog(@"syncSerial---begin");

dispatch_queue_tqueue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

dispatch_sync(queue, ^{

// 追加任务1

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_sync(queue, ^{

// 追加任务2

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_sync(queue, ^{

// 追加任务3

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

NSLog(@"syncSerial---end");}输出结果为:

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

在同步执行 + 串行队列可以看到:

a.所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(同步执行不具备开启新线程的能力)。

b.所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)。

c.任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。

4.4 异步执行 + 串行队列

会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务/**

* 异步执行 + 串行队列

* 特点:会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务。

*/

- (void)asyncSerial {

NSLog(@"currentThread---%@",[NSThreadcurrentThread]);// 打印当前线程

NSLog(@"asyncSerial---begin");

dispatch_queue_tqueue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);

dispatch_async(queue, ^{

// 追加任务1

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_async(queue, ^{

// 追加任务2

for(int i =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_async(queue, ^{

// 追加任务3

for(int i =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

NSLog(@"asyncSerial---end");}输出结果为:

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)}

在异步执行 + 串行队列可以看到:

a.开启了一条新线程(异步执行具备开启新线程的能力,串行队列只开启一个线程)。

b.所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。

c.任务是按顺序执行的(串行队列每次只有一个任务被执行,任务一个接一个按顺序执行)。

下边讲讲刚才我们提到过的特殊队列:主队列。

主队列:GCD自带的一种特殊的串行队列

所有放在主队列中的任务,都会放到主线程中执行

可使用dispatch_get_main_queue()获得主队列

我们再来看看主队列的两种组合方式。

4.5 同步执行 + 主队列

同步执行 + 主队列在不同线程中调用结果也是不一样,在主线程中调用会出现死锁,而在其他线程中则不会。

4.5.1 在主线程中调用同步执行 + 主队列

互相等待卡住不可行/**

* 同步执行 + 主队列

* 特点(主线程调用):互等卡主不执行。

* 特点(其他线程调用):不会开启新线程,执行完一个任务,再执行下一个任务。

*/

- (void)syncMain {

NSLog(@"currentThread---%@",[NSThreadcurrentThread]);

// 打印当前线程

NSLog(@"syncMain---begin");

dispatch_queue_tqueue = dispatch_get_main_queue();

dispatch_sync(queue, ^{

// 追加任务1

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"1---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_sync(queue, ^{

// 追加任务2

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"2---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

dispatch_sync(queue, ^{

// 追加任务3

for(inti =0; i <2; ++i) {

[NSThreadsleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"3---%@",[NSThreadcurrentThread]);// 打印当前线程

}

});

NSLog(@"syncMain---end");}输出结果

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)

在同步执行 + 主队列可以惊奇的发现:

在主线程中使用同步执行 + 主队列,追加到主线程的任务1、任务2、任务3都不再执行了,而且syncMain---end也没有打印,在XCode 9上还会报崩溃。这是为什么呢?

这是因为我们在主线程中执行syncMain方法,相当于把syncMain任务放到了主线程的队列中。而同步执行会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务1追加到主队列中,任务1就在等待主线程处理完syncMain任务。而syncMain任务需要等待任务1执行完毕,才能接着执行。

那么,现在的情况就是syncMain任务和任务1都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且syncMain---end也没有打印。

要是如果不在主线程中调用,而在其他线程中调用会如何呢?

4.5.2 在其他线程中调用同步执行 + 主队列

不会开启新线程,执行完一个任务,再执行下一个任务// 使用 NSThread 的 detachNewThreadSelector 方法会创建线程,并自动启动线程执行selector 任务[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:selfwithObject:nil];输出结果:

2018-02-23 20:44:19.377321+0800 YSC-GCD-demo[20083:5040347] currentThread---{number = 3, name = (null)}

2018-02-23 20:44:19.377494+0800 YSC-GCD-demo[20083:5040347] syncMain---begin

2018-02-23 20:44:21.384716+0800 YSC-GCD-demo[20083:5040132] 1---{number = 1, name = main}

2018-02-23 20:44:23.386091+0800 YSC-GCD-demo[20083:5040132] 1---{number = 1, name = main}

2018-02-23 20:44:25.387687+0800 YSC-GCD-demo[20083:5040132] 2---{number = 1, name = main}

2018-02-23 20:44:27.388648+0800 YSC-GCD-demo[20083:5040132] 2---{number = 1, name = main}

2018-02-23 20:44:29.390459+0800 YSC-GCD-demo[20083:5040132] 3---{number = 1, name = main}

2018-02-23 20:44:31.391965+0800 YSC-GCD-demo[20083:5040132] 3---{number = 1, name = main}

2018-02-23 20:44:31.392513+0800 YSC-GCD-demo[20083:5040347] syncMain---end

在其他线程中使用同步执行 + 主队列可看到:

a.所有任务都是在主线程(非当前线程)中执行的,没有开启新的线程(所有放在主队列中的任务,都会放到主线程中执行)。

b.所有任务都在打印的syncConcurrent---begin和syncConcurrent---end之间执行(同步任务需要等待队列的任务执行结束)。

c.任务是按顺序执行的(主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。

为什么现在就不会卡住了呢?

因为syncMain 任务放到了其他线程里,而任务1、任务2、任务3都在追加到主队列中,这三个任务都会在主线程中执行。syncMain任务在其他线程中执行到追加任务1到主队列中,因为主队列现在没有正在执行的任务,所以,会直接执行主队列的任务1,等任务1执行完毕,再接着执行任务2、任务3。所以这里不会卡住线程。

4.6 异步执行 + 主队列

只在主线程中执行任务,执行完一个任务,再执行下一个任务。/**

* 异步执行 + 主队列

* 特点:只在主线程中执行任务,执行完一个任务,再执行下一个任务

*/

- (void)asyncMain {

NSLog(@"currentThread---%@",[NSThreadcurrentThread]);// 打印当前线程

NSLog(@"asyncMain---begin");

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_async(queue, ^{

// 追加任务1

for(int i =0; i <2; ++i) {

[NSThread sleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"1---%@",[NSThread currentThread]);// 打印当前线程

}

});

dispatch_async(queue, ^{

// 追加任务2

for(int i =0; i <2; ++i) {

[NSThread sleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"2---%@",[NSThread currentThread]);// 打印当前线程

}

});

dispatch_async(queue, ^{

// 追加任务3

for(int i =0; i <2; ++i) {

[NSThread sleepForTimeInterval:2];// 模拟耗时操作

NSLog(@"3---%@",[NSThread currentThread]);// 打印当前线程

}

});

NSLog(@"asyncMain---end");}输出结果:

2018-02-23 20:45:49.981505+0800 YSC-GCD-demo[20111:5046708] currentThread---{number = 1, name = main}

2018-02-23 20:45:49.981935+0800 YSC-GCD-demo[20111:5046708] asyncMain---begin

2018-02-23 20:45:49.982352+0800 YSC-GCD-demo[20111:5046708] asyncMain---end

2018-02-23 20:45:51.991096+0800 YSC-GCD-demo[20111:5046708] 1---{number = 1, name = main}

2018-02-23 20:45:53.991959+0800 YSC-GCD-demo[20111:5046708] 1---{number = 1, name = main}

2018-02-23 20:45:55.992937+0800 YSC-GCD-demo[20111:5046708] 2---{number = 1, name = main}

2018-02-23 20:45:57.993649+0800 YSC-GCD-demo[20111:5046708] 2---{number = 1, name = main}

2018-02-23 20:45:59.994928+0800 YSC-GCD-demo[20111:5046708] 3---{number = 1, name = main}

2018-02-23 20:46:01.995589+0800 YSC-GCD-demo[20111:5046708] 3---{number = 1, name = main}

在异步执行 + 主队列可以看到:

a.所有任务都是在当前线程(主线程)中执行的,并没有开启新的线程(虽然异步执行具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中)。

b.所有任务是在打印的syncConcurrent---begin和syncConcurrent---end之后才开始执行的(异步执行不会做任何等待,可以继续执行任务)。

c.任务是按顺序执行的(因为主队列是串行队列,每次只有一个任务被执行,任务一个接一个按顺序执行)。

弄懂了难理解、绕来绕去的队列+任务之后,我们来学习一个简单的东西:5. GCD 线程间的通信。

c语言gcd 简易函数,简单[GCD]用法详细总结(上)相关推荐

  1. 函数指针c语言typedef,typedef 函数指针的用法(附例子)

    在网上搜索函数指针,看到一个例子.开始没看懂,想放弃,可是转念一想,这个用法迟早要弄懂的,现在多花点时间看懂它,好过以后碰到了要再花一倍时间来弄懂它.其实很多时候都是这样,如果每次到难一点的内容,总想 ...

  2. C语言。自定义函数简单版

    #include <stdio.h> //函数声明 void sayHi(); //函数实现 void sayHI() {printf("大家好!!\n"); } in ...

  3. R语言do.call函数简单说明

    Tips:可能有人对do.call函数不太了解, 这里简单举个例子说明此处的do.call使用效果. dat <- list(matrix(1:25, ncol = 5), matrix(4:2 ...

  4. c语言 内存清除函数,C语言内存初始化函数MEMSET的用法

    memset的功能是将内存块所指向的某一块内存中的每个字节的内容全部设置为指定的ASCII值,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作,其返回值为指向被初始化内存块的指针,它对 ...

  5. C语言socket connect()函数(初始化套接字上的连接)(未完)(如何测试socket是否已经断开,如何判断socket是否断开)

    参考文章:C网络编程socket之connect函数 需研究下这个函数超时多久才返回... 文章目录 项目中注释解释 man 2 文档解释 关于上面man 2 手册中所提到的connect()案例,在 ...

  6. 修饰符在python函数中的用法详细解释

    python修饰符是实现一个函数嵌套的功能,比较以下两个函数 # -*- coding:utf-8 -*- def a(fn):print 'a'def d(st):print "----- ...

  7. C++中函数模板的用法详细解析

    所谓函数模板实际上是建立一个通用函数,其涵涵素类型额形参类型不具体指定,用一个虚拟的类型来代表,这个通用函数就称为函数模板 定义 我们知道函数的重载可以实现一个函数名多用,将功能相同或者类似函数用同一 ...

  8. python中可以使用变量来引用函数吗_如何在python语言中使用函数变量并调用函数...

    在python语言中,除了常规变量之外,还有函数变量.把函数本身赋值给变量,这个变量为函数变量. 工具/原料 python pycharm 截图工具 WPS 方法/步骤 1 在已新建的python文件 ...

  9. php mysql字符串截取比较读取_MySQL_Mysql字符串截取函数SUBSTRING的用法说明,感觉上MySQL的字符串函数截取 - phpStudy...

    Mysql字符串截取函数SUBSTRING的用法说明 感觉上MySQL的字符串函数截取字符,比用程序截取(如PHP或JAVA)来得强大,所以在这里做一个记录,希望对大家有用. 函数: 1.从左开始截取 ...

  10. 满满干货赶紧进来!!!轻松掌握C语言中的函数

    摘要:本文对C语言中的函数进行全方面详细讲解,主要包括函数分类,函数参数,函数调用.函数声明和定义.函数递归.函数练习.全方面带你深度理解函数,耐心看完,相信看完这篇你会有不菲的收获~~ C语言-- ...

最新文章

  1. 72小时BCH日本生存挑战赛顺利完成
  2. VS2008环境下CEGUI 0.7.1及相关工具的编译(转载 + 额外的注意事项)
  3. 小白入门深度学习 | 第三篇:30分钟入门深度学习 - TensorFlow版
  4. html 保存xlsx,HTML SaveXLSX按钮防止将数据保存到SlickGrid的XLSX文件中
  5. Linux(四) 用户、用户组
  6. How to connect iOS simulator to Chrome for debugging
  7. ZOJ 3631 Watashi's BG
  8. java 调用groovy_Java调用Groovy脚本
  9. 驰骋开源软件一体化产品线,工作流程引擎,OA系统,即时通讯,单点登陆。
  10. Centos7升级gcc版本方法之一使用scl软件集
  11. SQLyog 安装教程
  12. Android拦截浏览器广告,浏览器拦截广告的几种方法
  13. 网络游戏封包基础知识
  14. Strom完整攻略(一)
  15. matlab中的颜色设置颜色,matlabpcolor设置颜色
  16. 大于23的男生女生都该看.看完你会变一个人【转】
  17. 趣头条递交招股书 将冲刺移动内容聚合第一股
  18. Save More Mice (贪心 二分)
  19. FTP服务搭建与配置
  20. Fundamental of 4G LTE - 学习笔记(3)OFDMA/OFDM in 4G LTE (Part1)

热门文章

  1. unity3D制作消消乐游戏/三消算法
  2. 网络互连(IPV4+NAT技术+ARP+IPV6)
  3. SpringCloud(8)— 使用ElasticSearch(RestClient)
  4. 2020-11-04 [蓝桥杯2018决赛]阅兵方阵
  5. PTA:7-1 哥尼斯堡的“七桥问题” (25 分)
  6. mysql amoeba_MySQL基于Amoeba实现读写分离
  7. Mysql查询性能优化-善用Explain语句
  8. 银行理财子与券商合作探讨(四):券商银行理财子数字化合作蓝图
  9. 联想电脑重装系统F12开机项选择无USB启动选项
  10. 服务器租用多少钱一年呢?