GCD知识梳理

GCDGrand Central Dispatch是苹果推出的的多线程管理框架, 也是开发中常用到的工具之一, 它具有自动管理线程的优势, 为开发工作提供了很大的便利。 GCD官方文档

队列

所谓队列就是指一连串的任务, 这些任务可以顺序执行, 也可以同时执行。这也就对应我们平时说的串行队列和并发队列。GCD官方文档一开始就给我们介绍了两种队列

dispatch_get_main_queue

1
2
// Returns the serial dispatch queue associated with the application’s main thread.
dispatch_queue_main_t dispatch_get_main_queue(void);

在主线程上运行的队列, 平时一般使用是在非主线程时回到主线程更新UI, 它是一个串行队列。

dispatch_get_global_queue

1
2
// Returns a system-defined global concurrent queue with the specified quality-of-service class.
dispatch_queue_global_t dispatch_get_global_queue(long identifier, unsigned long flags);

系统所定义的一种全局队列, 可以通过制定优先级去完成不同的任务, 它默认是一个并发队列。

同步和异步

简单来说, 同步是指在调用某个方法后, 必须得到返回的值, 才能进行下一步操作(会阻塞后续操作)。而异步和同步的不同是它不用得到返回的值可以继续后续操作(不会阻塞后面的操作)。

经典面试题, 下面代码将会输出什么?

1
2
3
4
5
6
7
__block NSInteger i = 0;
dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"%ld", i++);
});
NSLog(@"%ld", i++);

// 输出个锤子

来自官方文档的警告

Important

Attempting to synchronously execute a work item on the main queue results in deadlock.

如果换成dispatch_async则会正常输出

1
2
3
4
5
***** 时间23:44:34 GCDViewController.m 第24行 *****
0

***** 时间23:44:34 GCDViewController.m 第22行 *****
1

创建队列

1
dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

根据文档, 我们可以得出第一个参数是一个唯一标识符用于调试和定位, 也可以为空的, 第二个参数是队列类型, 有串行和并发。

有了队列之后, 我们就可以分别测试同步和异步在队列中的效果了, 使用[NSThread currentThread]来观察队列所在线程的情况

同步串行队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dispatch_queue_t syncSerialQueue = dispatch_queue_create("com.greg.syncserialqueue", DISPATCH_QUEUE_SERIAL);

NSLog(@"开始 %@", [NSThread currentThread]);

dispatch_sync(syncSerialQueue, ^{
    NSLog(@"同步 串行 1 %@", [NSThread currentThread]);
});

dispatch_sync(syncSerialQueue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"同步 串行 2 %@", [NSThread currentThread]);
});

NSLog(@"中间 %@", [NSThread currentThread]);

dispatch_sync(syncSerialQueue, ^{
    NSLog(@"同步 串行 3 %@", [NSThread currentThread]);
});

dispatch_sync(syncSerialQueue, ^{
    NSLog(@"同步 串行 4 %@", [NSThread currentThread]);
});

NSLog(@"结束 %@", [NSThread currentThread]);

执行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
***** 时间01:44:15 GCDViewController.m 第29行 *****
开始 <NSThread: 0x28091a4c0>{number = 1, name = main}

***** 时间01:44:15 GCDViewController.m 第32行 *****
同步 串行 1 <NSThread: 0x28091a4c0>{number = 1, name = main}

***** 时间01:44:17 GCDViewController.m 第37行 *****
同步 串行 2 <NSThread: 0x28091a4c0>{number = 1, name = main}

***** 时间01:44:17 GCDViewController.m 第40行 *****
中间 <NSThread: 0x28091a4c0>{number = 1, name = main}

***** 时间01:44:17 GCDViewController.m 第43行 *****
同步 串行 3 <NSThread: 0x28091a4c0>{number = 1, name = main}

***** 时间01:44:17 GCDViewController.m 第47行 *****
同步 串行 4 <NSThread: 0x28091a4c0>{number = 1, name = main}

***** 时间01:44:17 GCDViewController.m 第50行 *****
结束 <NSThread: 0x28091a4c0>{number = 1, name = main}

同步+并发 这种方式是完全按照顺序输出的

异步串行队列

由于是异步执行, 我们可以现在队列的加上开始中间和结束试一试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dispatch_queue_t asyncSerialQueue = dispatch_queue_create("com.greg.asyncserialqueue", DISPATCH_QUEUE_SERIAL);

NSLog(@"开始 %@", [NSThread currentThread]);

dispatch_async(asyncSerialQueue, ^{
    NSLog(@"异步 串行 1 %@", [NSThread currentThread]);
});
    
dispatch_async(asyncSerialQueue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"异步 串行 2 %@", [NSThread currentThread]);
});

NSLog(@"中间 %@", [NSThread currentThread]);

dispatch_async(asyncSerialQueue, ^{
    NSLog(@"异步 串行 3 %@", [NSThread currentThread]);
});

dispatch_async(asyncSerialQueue, ^{
    NSLog(@"异步 串行 4 %@", [NSThread currentThread]);
});

NSLog(@"结束 %@", [NSThread currentThread]);

经过多次的运行后, 有时会出现这样的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
***** 时间01:49:49 GCDViewController.m 第29行 *****
开始 <NSThread: 0x283e8d840>{number = 1, name = main}

***** 时间01:49:49 GCDViewController.m 第40行 *****
中间 <NSThread: 0x283e8d840>{number = 1, name = main}

***** 时间01:49:49 GCDViewController.m 第32行 *****
异步 串行 1 <NSThread: 0x283ed4880>{number = 5, name = (null)}

***** 时间01:49:49 GCDViewController.m 第50行 *****
结束 <NSThread: 0x283e8d840>{number = 1, name = main}

***** 时间01:49:51 GCDViewController.m 第37行 *****
异步 串行 2 <NSThread: 0x283ed4880>{number = 5, name = (null)}

***** 时间01:49:51 GCDViewController.m 第43行 *****
异步 串行 3 <NSThread: 0x283ed4880>{number = 5, name = (null)}

***** 时间01:49:51 GCDViewController.m 第47行 *****
异步 串行 4 <NSThread: 0x283ed4880>{number = 5, name = (null)}

异步+串行 这种方式的任务也是一个一个执行的, 即使有堵塞也要等待, 但有一个问题, 异步执行开启了新线程, 可以看到中间异步 串行1同时执行了

同步并发队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dispatch_queue_t syncConcurrentQueue = dispatch_queue_create("com.greg.syncconcurrentqueue", DISPATCH_QUEUE_CONCURRENT);

NSLog(@"开始 %@", [NSThread currentThread]);

dispatch_sync(syncConcurrentQueue, ^{
    NSLog(@"同步 并发 1 %@", [NSThread currentThread]);
});

dispatch_sync(syncConcurrentQueue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"同步 并发 2 %@", [NSThread currentThread]);
});

NSLog(@"中间 %@", [NSThread currentThread]);

dispatch_sync(syncConcurrentQueue, ^{
    NSLog(@"同步 并发 3 %@", [NSThread currentThread]);
});

dispatch_sync(syncConcurrentQueue, ^{
    NSLog(@"同步 并发 4 %@", [NSThread currentThread]);
});

NSLog(@"结束 %@", [NSThread currentThread]);

多次执行后的输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
***** 时间01:37:45 GCDViewController.m 第29行 *****
开始 <NSThread: 0x281512300>{number = 1, name = main}

***** 时间01:37:45 GCDViewController.m 第32行 *****
同步 并发 1 <NSThread: 0x281512300>{number = 1, name = main}

***** 时间01:37:47 GCDViewController.m 第37行 *****
同步 并发 2 <NSThread: 0x281512300>{number = 1, name = main}

***** 时间01:37:47 GCDViewController.m 第40行 *****
中间 <NSThread: 0x281512300>{number = 1, name = main}

***** 时间01:37:47 GCDViewController.m 第43行 *****
同步 并发 3 <NSThread: 0x281512300>{number = 1, name = main}

***** 时间01:37:47 GCDViewController.m 第47行 *****
同步 并发 4 <NSThread: 0x281512300>{number = 1, name = main}

***** 时间01:37:47 GCDViewController.m 第50行 *****
结束 <NSThread: 0x281512300>{number = 1, name = main}

同步+并发 这种方式是完全按照顺序输出的, 有可能有理解误区在并发上面, 实际上都是在主线程执行, 并没有多线程执行

异步并发队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dispatch_queue_t asyncConcurrentQueue = dispatch_queue_create("com.greg.asyncconcurrentlqueue", DISPATCH_QUEUE_CONCURRENT);

NSLog(@"开始 %@", [NSThread currentThread]);

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"异步 并发 1 %@", [NSThread currentThread]);
});

dispatch_async(asyncConcurrentQueue, ^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"异步 并发 2 %@", [NSThread currentThread]);
});

NSLog(@"中间 %@", [NSThread currentThread]);

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"异步 并发 3 %@", [NSThread currentThread]);
});

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"异步 并发 4 %@", [NSThread currentThread]);
});

NSLog(@"结束 %@", [NSThread currentThread]);

这次输出的结果就比较凌乱了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
***** 时间01:39:22 GCDViewController.m 第29行 *****
开始 <NSThread: 0x2800c6100>{number = 1, name = main}

***** 时间01:39:22 GCDViewController.m 第40行 *****
中间 <NSThread: 0x2800c6100>{number = 1, name = main}

***** 时间01:39:22 GCDViewController.m 第32行 *****
异步 并发 1 <NSThread: 0x280090380>{number = 5, name = (null)}

***** 时间01:39:22 GCDViewController.m 第50行 *****
结束 <NSThread: 0x2800c6100>{number = 1, name = main}

***** 时间01:39:22 GCDViewController.m 第43行 *****
异步 并发 3 <NSThread: 0x28009b700>{number = 6, name = (null)}

***** 时间01:39:22 GCDViewController.m 第47行 *****
异步 并发 4 <NSThread: 0x28009b700>{number = 6, name = (null)}

***** 时间01:39:24 GCDViewController.m 第37行 *****
异步 并发 2 <NSThread: 0x280090380>{number = 5, name = (null)}

异步+并发这种方式是真正的并发+多线程, 有堵塞的任务会最后执行, 且线程都不相同

并发队列是否可以有序执行?

答案是可以的。可以利用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
dispatch_queue_t asyncConcurrentQueue = dispatch_queue_create("com.greg.asyncconcurrentqueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"异步执行1")
});

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"异步执行2")
});
    
dispatch_barrier_async(asyncConcurrentQueue, ^{
    NSLog(@"异步栅栏 1");
});

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"异步执行3")
});

dispatch_barrier_sync(asyncConcurrentQueue, ^{
    NSLog(@"同步栅栏 1");
});

NSLog(@"结束了");

结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
***** 时间15:46:04 SDImageLoadViewController.m 第57行 *****
GQLOG: 异步执行2

***** 时间15:46:04 SDImageLoadViewController.m 第53行 *****
GQLOG: 异步执行1

***** 时间15:46:04 SDImageLoadViewController.m 第61行 *****
GQLOG: 异步栅栏 1

***** 时间15:46:04 SDImageLoadViewController.m 第65行 *****
GQLOG: 异步执行3

***** 时间15:46:04 SDImageLoadViewController.m 第69行 *****
GQLOG: 同步栅栏 1

***** 时间15:46:04 SDImageLoadViewController.m 第72行 *****
GQLOG: 结束了

异步栅栏可以分割异步任务, 及异步执行1,2 与异步执行3, 同步栅栏可以起到线程同步的作用, 即最后打印”结束了”。

小结

  • 同步执行, 不会开启新的线程, 无论是串行或并发的同步任务, 都以串行的方式执行
  • 异步执行, 会开启新的线程, 串行即一个一个的执行, 并发即一起执行

调度组

多条队列是可以有顺序执行的。

队列的优先级

1
dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t attr, dispatch_qos_class_t qos_class, int relative_priority);

观察这样的一段代码

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
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, -1);

dispatch_queue_t asyncSerialQueue = dispatch_queue_create("com.greg.asyncserialqueue", attr);

dispatch_queue_attr_t otherAttr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INTERACTIVE, -1);

dispatch_queue_t asyncConcurrentQueue = dispatch_queue_create("com.greg.asyncconcurrentqueue", otherAttr);


dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"队列2 %@", [NSThread currentThread]);
});

dispatch_async(asyncSerialQueue, ^{
    NSLog(@"队列 -1 %@", [NSThread currentThread]);
});

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"队列3 %@", [NSThread currentThread]);
});

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"队列4 %@", [NSThread currentThread]);
});

dispatch_async(asyncConcurrentQueue, ^{
    NSLog(@"队列1 %@", [NSThread currentThread]);
});

队列 2,3,4 总是会优先于 1和-1 执行, 无论他们是什么类型的队列, 起决定性的参数为dispatch_queue_attr_make_with_qos_class中的第二个参数, 文档中代码的顺序表示优先级从高到低。

但这只是默认的顺序执行而已, 如果上面其中有一个队列的任务堵塞, 其他队列的执行是不受影响的, 如果要控制所有队列的执行顺序就需要用到调度组。

dispatch_group_t

为了方便队列管理, 苹果推出了调度组, 方便我们进行队列管理。一个调度组的基本写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
dispatch_queue_t asyncConcurrentQueue = dispatch_queue_create("com.greg.asyncconcurrentqueue", DISPATCH_QUEUE_CONCURRENT);
    
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, asyncConcurrentQueue, ^{
    NSLog(@"组 异步 串行 1 %@", [NSThread currentThread]);
    NSLog(@"组 异步 串行 2 %@", [NSThread currentThread]);
});

dispatch_group_async(group, asyncConcurrentQueue, ^{
    NSLog(@"组 异步 串行 3 %@", [NSThread currentThread]);
    NSLog(@"组 异步 串行 4 %@", [NSThread currentThread]);
});

统一回调

dispatch_group_wait

有些时候我们希望调度组中的任务执行完毕之后统一来做一些事情, 对于上面这种block内部完全是同步执行的, 可以用long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);来进行等待操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
dispatch_queue_t asyncConcurrentQueue = dispatch_queue_create("com.greg.asyncconcurrentqueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, asyncConcurrentQueue, ^{
    NSLog(@"组 异步 串行 1 %@", [NSThread currentThread]);
    NSLog(@"组 异步 串行 2 %@", [NSThread currentThread]);
});

dispatch_group_async(group, asyncConcurrentQueue, ^{
    NSLog(@"组 异步 串行 3 %@", [NSThread currentThread]);
    NSLog(@"组 异步 串行 4 %@", [NSThread currentThread]);
});

dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 会阻塞

NSLog(@"全部结束 %@", [NSThread currentThread]);

如果上面的其中一个dispatch_group_async有异步执行, 就不能再用这个函数了。实际上我们所遇到的更多也是异步执行任务, 那么就要用到下面的方法:

dispatch_group_notify

这个方法还与另外两个方法有关联, 他们是dispatch_group_enterdispatch_group_leave, 我们以网络请求为例子:

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
dispatch_group_t group = dispatch_group_create();

dispatch_group_enter(group);
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com/"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSLog(@"百度开始响应 = %ld", [(NSHTTPURLResponse *)response statusCode]);
    dispatch_group_leave(group);
}];
[task resume];

dispatch_group_enter(group);
NSURLSessionDataTask *task1 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.163.com/"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSLog(@"网易开始响应 = %ld", [(NSHTTPURLResponse *)response statusCode]);
    dispatch_group_leave(group);
}];
[task1 resume];

dispatch_group_enter(group);
NSURLSessionDataTask *task2 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.qq.com/"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSLog(@"企鹅开始响应 = %ld", [(NSHTTPURLResponse *)response statusCode]);
    dispatch_group_leave(group);
}];
[task2 resume];

dispatch_group_enter(group);
NSURLSessionDataTask *task3 = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.sina.com.cn/"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    NSLog(@"新浪开始响应 = %ld", [(NSHTTPURLResponse *)response statusCode]);
    dispatch_group_leave(group);
}];
[task3 resume];

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"全部结束");
});

注意添加和离开要成对出现, 当所有的离开都执行完毕字后, 会调用dispatch_group_notify来执行统一回调。 结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
***** 时间16:03:19 GCDViewController.m 第62行 *****
企鹅开始响应 = 200

***** 时间16:03:19 GCDViewController.m 第48行 *****
百度开始响应 = 200

***** 时间16:03:19 GCDViewController.m 第69行 *****
新浪开始响应 = 200

***** 时间16:03:20 GCDViewController.m 第55行 *****
网易开始响应 = 200

***** 时间16:03:20 GCDViewController.m 第75行 *****
全部结束

dispatch_semaphore_t

上面的例子适用于多种任务同时执行之后的回调, 但是现在有这么一种情况, 我想只做一件事情, 而且这件事情要做好多次, 在达到某个节点的时候进行回调, 有没有更好的解决方式呢? 这个时候就会用到另一个对象: 信号量。

1
2
3
4
5
6
7
8
9
10
11
12
13
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSession *session = [NSURLSession sharedSession];
for (int i = 0 ; i < 5; i++) {
    NSLog(@"%d", i);
    NSURLSessionDataTask *task = [session dataTaskWithURL:[NSURL URLWithString:@"https://www.baidu.com/"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSLog(@"百度开始响应 = %ld", [(NSHTTPURLResponse *)response statusCode]);
        dispatch_semaphore_signal(semaphore);
    }];
    [task resume];
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}

NSLog(@"结束了");

上述例子中, 下标的打印和请求成功的打印总是会同对出现, 而且是顺序的:

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
***** 时间16:31:14 GCDViewController.m 第50行 *****
0

***** 时间16:31:14 GCDViewController.m 第52行 *****
百度开始响应 = 200

***** 时间16:31:14 GCDViewController.m 第50行 *****
1

***** 时间16:31:14 GCDViewController.m 第52行 *****
百度开始响应 = 200

***** 时间16:31:14 GCDViewController.m 第50行 *****
2

***** 时间16:31:14 GCDViewController.m 第52行 *****
百度开始响应 = 200

***** 时间16:31:14 GCDViewController.m 第50行 *****
3

***** 时间16:31:14 GCDViewController.m 第52行 *****
百度开始响应 = 200

***** 时间16:31:14 GCDViewController.m 第50行 *****
4

***** 时间16:31:14 GCDViewController.m 第52行 *****
百度开始响应 = 200

***** 时间16:31:14 GCDViewController.m 第59行 *****
结束了

信号量总结: 当调用dispatch_semaphore_wait会减少信号量计数, 当信号量为0的情况下会阻塞当前线程, 而dispatch_semaphore_signal会增加信号量计数, 利用这个特性我们仍然可以等所有异步操作完成之后再去做一些事情, 但弊端就是会堵塞当前线程。

其他方法

dispatch_after

延迟执行放方法dispatch_after

1
2
3
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // 延迟一秒执行        
});

dispatch_once

1
2
3
4
5
只执行一次, 适用于单例模式
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    
});

dispatch_apply

这个东西类似于迭代器, 一个block中的方法会执行指定的次数:

1
2
3
4
5
 dispatch_queue_t queue = dispatch_queue_create("com.greg.applytest", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t i) {
    NSLog(@"%@ %zu", [NSThread currentThread], i);
});
NSLog(@"结束了");

注意它的线程输出:

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
***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282333080>{number = 1, name = main} 0

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282354300>{number = 3, name = (null)} 1

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282333080>{number = 1, name = main} 2

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282354300>{number = 3, name = (null)} 3

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282333080>{number = 1, name = main} 4

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282354300>{number = 3, name = (null)} 5

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282333080>{number = 1, name = main} 6

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282354300>{number = 3, name = (null)} 7

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282354300>{number = 3, name = (null)} 9

***** 时间15:49:28 GCDViewController.m 第63行 *****
<NSThread: 0x282333080>{number = 1, name = main} 8

***** 时间15:49:28 GCDViewController.m 第65行 *****
结束了

在主线程和自己创建的线程之间交互输出的, 并且会阻塞当前线程。 试一下在异步并发中执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
dispatch_queue_t queue = dispatch_queue_create("com.greg.applytest", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"开始了");
    NSLog(@"%@", [NSThread currentThread]);
    dispatch_apply(10, queue, ^(size_t i) {
        NSLog(@"%@ %zu", [NSThread currentThread], i);
    });
    NSLog(@"中间");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"回到主线程");
    });
});
NSLog(@"结束了");

结果:

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
35
36
37
38
39
40
41
42
43
44
***** 时间15:54:46 GCDViewController.m 第73行 *****
结束了

***** 时间15:54:46 GCDViewController.m 第63行 *****
开始了

***** 时间15:54:46 GCDViewController.m 第64行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)}

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 0

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 1

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 2

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 3

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 4

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 5

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 6

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 7

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 8

***** 时间15:54:46 GCDViewController.m 第66行 *****
<NSThread: 0x28110de40>{number = 5, name = (null)} 9

***** 时间15:54:46 GCDViewController.m 第68行 *****
中间

***** 时间15:54:46 GCDViewController.m 第70行 *****
回到主线程

这样就全在一条队列上执行了。

其余的待补充中。。。