多线程编程-GCD

进程

定义:

1
2
3
在进程模型中,计算机上所有可运行的软件,通常也包括操作系统,被组织成若干顺序进程,简称进程(process)。

--摘自《现代操作系统》
1
2
3
4
狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

--摘自《百度百科》

在iOS系统中,开启一个应用就打开了一个进程。

进程和程序之间的区别:

1
2
进程就是厨师阅读蛋糕食谱,取来各种原料以及烘制蛋糕的一系列动作的总和。
其中的厨师相当于处理器(CPU),蛋糕食谱相当于程序(即用适当形式描述的算法),各种原料相当于输入的数据。

创建进程的4个主要事件:

1
2
3
4
1. 系统初始化
2. 执行了正在运行的进程所调用的进程创建系统调用
3. 用户请求创建一个新进程
4. 一个批处理作业的初始化

进程的三种状态及转换关系:

1
2
3
4
1. 在操作系统发现进程不能继续运行下去,发生转换1
2. 2和3的转换是由进程调度程序引起的。系统认为一个运行进程占用时间过长,
决定让其他进程来使用cpu,会发生转换2。系统重新让刚刚的进程重新运行发生转换3
3. 当进程等待一个外部事件发生时(例如一些输入),发生转换4

线程

定义:

1
2
3
4
5
6
7
8
线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,
是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,
每条线程并行执行不同的任务。
在Unix System V及SunOS中也被称为轻量进程,但轻量进程更多指内核线程,
而把用户线程称为线程。

--摘自《百度百科》

在iOS系统中,一个进程包含一个主线程,它的主要任务是处理UI事件,显示和刷新UI。

进程和线程的关系:

1
2
3
4
5
6
1. 进程是CPU调度和资源分配的基本单位。
2. 同一进程内的线程共享进程的资源。
3. 线程是由相关堆栈寄存器和线程控制表组成。
4. 一个进程可以包含若干线程,每个线程为独立运行和调用的基本单位。
5. 由于线程的调度消耗比进程更小,因此能高效的提高系统内多个程序间的并发执行程度,
从而提高系统的资源利用率和吞吐量。

区别:

1
2
3
4
1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
2)通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
3)调度和切换:线程上下文切换比进程上下文切换要快得多。
4)在多线程OS中,线程不是一个可执行的实体。

同步 && 异步

同步

在当前线程中依次执行任务,不开启新的线程。

异步

多个任务情况下,一个任务A正在执行,同时可以执行另一个任务B。任务B不用等待任务A结束才执行。存在多条线程。

并发 && 并行

并发

并发指当多个线程操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。这种方式我们称之为并发(Concurrent)。

并行

并行指当系统有一个以上CPU时,其中一个CPU执行一个线程时,另一个CPU执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。

总结:

并行是指两个或者多个事件在同一时刻发生, 而并发是指两个或多个事件在同一时间间隔内发生。在多程序环境下,并发性是指在一段时间内宏观上有多个程序在同时运行,但在单处理机系统中,每一时刻却只能有一道程序执行,因此这些程序只能是分时地交替执行。倘若该计算机系统有多个处理器,则这些可以并发执行的程序便可被分配到多个处理器上,实现并行执行,这样,多个程序便可以同时执行。

Grand Central Dispatch(GCD)

百度百科的定义:

1
2
3
4
Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。
它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池
模式的基础上执行的并发任务。在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4
及以上版本使用。

GCD线程的简单使用:

1
2
3
GCD 可用于多核的并行运算
GCD 会自动利用更多的 CPU 内核(比如双核、四核)
GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

两种队列的创建/获取方式

并发队列
1
2
3
4
dispatch_queue_t queue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);

默认的全局并发队列
dispatch_queue_t global_queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
串行队列
1
2
3
4
dispatch_queue_t queue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);

主队列:特殊的串行队列
dispatch_queue_t main_serial_queue = dispatch_get_main_queue();

队列和线程的6种组合

并发队列(CONCURRENT) 串行队列(SERIAL) 主队列
同步(sync) 没有开启新线程,串行执行任务 没有开启新线程,串行执行任务 (主线程调用:死锁)其他线程:没有开启新线程,串行执行任务
异步(async) 开启新线程,并发执行任务 开启新线程(1条),串行执行任务 没有开启新线程,串行执行任务
1.同步+并发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("com.concurrent.sync.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});

dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");

打印结果:

1
2
3
4
5
6
7
[18151:827307] currentThread---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] syncConcurrent---begin
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] syncConcurrent---end
2.异步+并发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"asyncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("com.concurrent.async.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});

dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@",[NSThread currentThread]);
}
});
NSLog(@"asyncConcurrent---end");

打印结果:

1
2
3
4
5
6
7
[18151:827307] currentThread---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] asyncConcurrent---begin
[18151:827307] asyncConcurrent---end
[18151:828000] 1---<NSThread: 0x600002370c00>{number = 3, name = (null)}
[18151:831272] 2---<NSThread: 0x60000238e000>{number = 4, name = (null)}
[18151:828000] 1---<NSThread: 0x600002370c00>{number = 3, name = (null)}
[18151:831272] 2---<NSThread: 0x60000238e000>{number = 4, name = (null)}
3.同步+串行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"syncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("com.serial.sync.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});

dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@",[NSThread currentThread]);
}
});
NSLog(@"syncSerial---end");

打印结果:

1
2
3
4
5
6
7
[18151:827307] currentThread---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] syncSerial---begin
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] syncSerial---end
4.异步+串行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"asyncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("com.serial.async.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});

dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2---%@",[NSThread currentThread]);
}
});
NSLog(@"asyncSerial---end");

打印结果:

1
2
3
4
5
6
7
[18151:827307] currentThread---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] asyncSerial---begin
[18151:827307] asyncSerial---end
[18151:831273] 1---<NSThread: 0x60000238d940>{number = 5, name = (null)}
[18151:831273] 1---<NSThread: 0x60000238d940>{number = 5, name = (null)}
[18151:831273] 2---<NSThread: 0x60000238d940>{number = 5, name = (null)}
[18151:831273] 2---<NSThread: 0x60000238d940>{number = 5, name = (null)}
5.同步+主队列
1
2
3
4
5
6
7
8
9
10
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"syncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});
NSLog(@"syncMain---end");

如果在主线程中执行该代码打印如下结果后直接卡死

1
2
[18151:831273] currentThread---<NSThread: 0x60000238d940>{number = 5, name = (null)}
[18151:831273] syncMain---begin

我们可创建一个异步线程,在异步线程中重新执行上述代码

1
2
3
4
dispatch_queue_t queue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
...
});

结果如下:

1
2
3
4
5
6
7
[18151:831273] currentThread---<NSThread: 0x60000238d940>{number = 5, name = (null)}
[18151:831273] syncMain---begin
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:831273] syncMain---end
6.异步+主队列
1
2
3
4
5
6
7
8
9
10
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"asyncMain---begin");
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
}
});
NSLog(@"asyncMain---end");

打印结果:

1
2
3
4
5
6
7
[18151:827307] currentThread---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] asyncMain---begin
[18151:827307] asyncMain---end
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 1---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}
[18151:827307] 2---<NSThread: 0x6000023f6900>{number = 1, name = main}

其他方法

  1. 栅栏方法: dispatch_barrier_async
  2. 延时执行方法: dispatch_after
  3. 一次性代码: dispatch_once
  4. 快速迭代方法: dispatch_apply
  5. 信号量: dispatch_semaphore
  6. 队列组: dispatch_group_notify、dispatch_group_wait、和
    dispatch_group_enter、dispatch_group_leave (两者组合等同于 dispatch_group_async)
  7. 队列标识: dispatch_queue_set_specific

1. 栅栏方法: dispatch_barrier_async

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"barrier async---begin");
dispatch_queue_t queue = dispatch_queue_create("com.concurrent.async.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"11---%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"22---%@",[NSThread currentThread]);
}
});

// 栅栏方法
dispatch_barrier_async(queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"barrier---%@",[NSThread currentThread]);
}
});

NSLog(@"barrier async---end");

执行结果:

1
2
3
4
5
6
7
8
9
[18339:884102] currentThread---<NSThread: 0x6000039c9380>{number = 1, name = main}
[18339:884102] barrier async---begin
[18339:884102] barrier async---end
[18339:884207] 22---<NSThread: 0x60000394dfc0>{number = 3, name = (null)}
[18339:884208] 11---<NSThread: 0x6000039b1a40>{number = 4, name = (null)}
[18339:884207] 22---<NSThread: 0x60000394dfc0>{number = 3, name = (null)}
[18339:884208] 11---<NSThread: 0x6000039b1a40>{number = 4, name = (null)}
[18339:884208] barrier---<NSThread: 0x6000039b1a40>{number = 4, name = (null)}
[18339:884208] barrier---<NSThread: 0x6000039b1a40>{number = 4, name = (null)}

5. 信号量: dispatch_semaphore

1
2
3
dispatch_semaphore_create:创建一个Semaphore并初始化信号的总量
dispatch_semaphore_signal:发送一个信号,让信号总量加1
dispatch_semaphore_wait:当信号总量为0时就会一直等待(阻塞所在线程)
1
2
3
4
5
6
7
8
9
10
11
12
13
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"semaphore---begin");
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block NSInteger number = 0;
dispatch_async(queue, ^{
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@",[NSThread currentThread]);
number = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore---end,number = %ld",(long)number);

执行结果:

1
2
3
4
[18361:888102] currentThread---<NSThread: 0x6000021b68c0>{number = 1, name = main}
[18361:888102] semaphore---begin
[18361:888147] 1---<NSThread: 0x6000021323c0>{number = 5, name = (null)}
[18361:888102] semaphore---end,number = 100

6. 队列组: dispatch_group

6.1 dispatch_group_async
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"11---%@",[NSThread currentThread]);
}
});

dispatch_group_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"22---%@",[NSThread currentThread]);
}
});
6.2 dispatch_group_enter,dispatch_group_leave

两者成对出现,如果只是用 dispatch_group_enter,会使线程一直阻塞。
如果只使用dispatch_group_leave会出现野指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
NSLog(@"currentThread---%@",[NSThread currentThread]);
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"11---%@",[NSThread currentThread]);
}
dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(group, queue, ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"22---%@",[NSThread currentThread]);
}
dispatch_group_leave(group);
});

以下方法接上面的 6.1 或者 6.2

方法1. dispatch_group_notify,不会阻塞主线程,但是会等任务1、任务2都执行完毕后,回到原线程中执行notify内容
1
2
3
4
5
6
7
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2];
NSLog(@"33---%@",[NSThread currentThread]);
}
});
NSLog(@"group---end");
方法1结果如下:
1
2
3
4
5
6
7
8
9
[18361:888102] currentThread---<NSThread: 0x6000021b68c0>{number = 1, name = main}
[18361:888102] group---begin
[18361:888102] group---end
[18361:888147] 11---<NSThread: 0x6000021323c0>{number = 5, name = (null)}
[18361:889748] 22---<NSThread: 0x600002132200>{number = 6, name = (null)}
[18361:889748] 22---<NSThread: 0x600002132200>{number = 6, name = (null)}
[18361:888147] 11---<NSThread: 0x6000021323c0>{number = 5, name = (null)}
[18361:888102] 33---<NSThread: 0x6000021b68c0>{number = 1, name = main}
[18361:888102] 33---<NSThread: 0x6000021b68c0>{number = 1, name = main}
方法2. dispatch_group_wait,会阻塞当前线程,等前面的异步任务1、任务2都执行完毕才会接着执行
1
2
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
方法2结果如下:
1
2
3
4
5
6
7
[18361:888102] currentThread---<NSThread: 0x6000021b68c0>{number = 1, name = main}
[18361:888102] group---begin
[18361:888147] 11---<NSThread: 0x6000021323c0>{number = 5, name = (null)}
[18361:889748] 22---<NSThread: 0x600002132200>{number = 6, name = (null)}
[18361:889748] 22---<NSThread: 0x600002132200>{number = 6, name = (null)}
[18361:888147] 11---<NSThread: 0x6000021323c0>{number = 5, name = (null)}
[18361:888102] group---end

7. 队列标识: dispatch_queue_set_specific

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void *qspecific_queue_key = "qspecific_queue_key";
dispatch_queue_t specific_queue = dispatch_queue_create(qspecific_queue_key, DISPATCH_QUEUE_SERIAL);
dispatch_queue_set_specific(specific_queue, qspecific_queue_key, &qspecific_queue_key, NULL);
NSLog(@"1. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
//当前队列是主队列,不是 specific_queue 队列,所以取不到 specific_queue 对应的值 因此执行 else
if (dispatch_get_specific(qspecific_queue_key)) {
NSLog(@"2. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
[NSThread sleepForTimeInterval:1];
}
else {
NSLog(@"3. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
[NSThread sleepForTimeInterval:1];
}

dispatch_sync(specific_queue, ^{
NSLog(@"4. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
[NSThread sleepForTimeInterval:1];
//当前队列是 specific_queue 队列,所以能取到 specific_queue 对应的值,执行 if
if (dispatch_get_specific(qspecific_queue_key)) {
NSLog(@"5. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
[NSThread sleepForTimeInterval:1];
}
else {
NSLog(@"6. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
[NSThread sleepForTimeInterval:1];
}
});

dispatch_async(specific_queue, ^{
NSLog(@"7. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
[NSThread sleepForTimeInterval:1];
});
NSLog(@"8. 当前线程是: %@, 当前队列是: %@ 。",[NSThread currentThread], dispatch_get_current_queue());
[NSThread sleepForTimeInterval:5];

输出结果如下:

1
2
3
4
5
6
[3486:89682] 1. 当前线程是: <NSThread: 0x7f8fc8e04c20>{number = 1, name = main}, 当前队列是: <OS_dispatch_queue_main: com.apple.main-thread> 。
[3486:89682] 3. 当前线程是: <NSThread: 0x7f8fc8e04c20>{number = 1, name = main}, 当前队列是: <OS_dispatch_queue_main: com.apple.main-thread> 。
[3486:89682] 4. 当前线程是: <NSThread: 0x7f8fc8e04c20>{number = 1, name = main}, 当前队列是: <OS_dispatch_queue_serial: qspecific_queue_key> 。
[3486:89682] 5. 当前线程是: <NSThread: 0x7f8fc8e04c20>{number = 1, name = main}, 当前队列是: <OS_dispatch_queue_serial: qspecific_queue_key> 。
[3486:89682] 8. 当前线程是: <NSThread: 0x7f8fc8e04c20>{number = 1, name = main}, 当前队列是: <OS_dispatch_queue_main: com.apple.main-thread> 。
[3486:89717] 7. 当前线程是: <NSThread: 0x7f8fc8d43b70>{number = 2, name = (null)}, 当前队列是: <OS_dispatch_queue_serial: qspecific_queue_key> 。