0%

GCD

基本的 GCD 使用

同步异步 \ 队列 全局并行队列 手动创建的串行队列 主队列
同步(sync) 没有开启新线程
串行执行任务
没有开启新线程
串行执行任务
死锁
异步(async) 开启新线程
并行执行任务
开启新线程
串行执行任务
没有开启新线程
串行执行任务

串行队列

创建串行队列

在使用 GCD 的时候先写 dispatch

在 C 语言中,定义对象通常是以_t或者Ref结尾的

特点:依次顺序执行

在非 ARC 中需要释放队列:dispatch_release(q)

DISPATCH_QUEUE_SERIAL 串行 (宏定义了:#define DISPATCH_QUEUE_SERIAL NULL)

DISPATCH_QUEUE_CONCURRENT 并行

1
2
dispatch_queue_t q = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
// 等同于dispatch_queue_t q = dispatch_queue_create("queueName", NULL);

创建任务

同步任务

不会开启新的线程

dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
串行队列中,同步任务在主线程工作,所有任务顺序执行

1
2
3
4
5
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}
异步任务

只能开一条线程

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

串行队列中,异步任务最多只能开一条线程,所有任务顺序执行

串行队列,异步任务,在多线程中,是斯坦福大学最推荐的一种多线程方式

优点:将任务放在其他线程中工作,每个任务顺序执行

缺点:并发能力不强,最多只能使用一条线程

1
2
3
4
5
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}

并行队列

创建并行队列

dispatch_queue_t q = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);

创建任务

同步任务

不会开启新的线程

dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);

在实际开发中,同步任务可以保证执行完成之后,才让后续的异步任务开始执行,用于控制任务之间的先后顺序

1
2
3
4
5
6
NSLog(@"================2.1同步任务================");
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}
异步任务

会在多条线程上工作,具体开多少条线程,由系统决定

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

仍然是按照任务添加到队列中的顺序被调度,知识执行先后可能会有差异

能够将耗时的操作放到子线程中工作

与串行队列相比,开启多条线程(系统决定),并发性能更好,但是执行的先后顺序不固定

1
2
3
4
5
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}

全局并行(并发)队列(使用的更为普遍)

与自定义并行队列的区别就是名字不同,其他完全相同

获取全局队列

与自定义并行队列的区别就是名字不同,其他相同

dispatch_queue_t q = dispatch_get_global_queue(long identifier, unsigned long flags);

关于dispatch_queue_priority_t

1
2
3
4
#define DISPATCH_QUEUE_PRIORITY_HIGH        2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
1
2
3
4

```objc
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//等同于dispatch_queue_t q = dispatch_get_global_queue(0, 0);

创建任务

同步任务
1
2
3
4
5
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}
异步任务
1
2
3
4
5
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}

主队列

专门在主线程上工作的队列,不能开线程

获取主线程

1
dispatch_queue_t q = dispatch_get_main_queue();

创建任务

同步任务

注意:不要使用同步任务,会发生死锁

1
2
3
4
5
for (int i = 0; i < 10; i++) {
dispatch_sync(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}
异步任务,在主线程上依次顺序执行
1
2
3
4
5
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%zd %@",i , [NSThread currentThread]);
});
}

GCD 多线程同步等问题

串行队列中可以将一个任务放到另一个任务完成之后继续,这里讨论并行队列同步问题。

dispatch_barrier_async

dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);

dispatch_barrier_async必定在前面创建的任务运行完之后才会调用,并且其之后创建的任务必定在其运行完之后才会运行,与 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
26
27
28
dispatch_queue_t concurrentQueue = dispatch_queue_create("tbd.tech", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-1");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-2");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-3");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-4");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-5");
});

// dispatch_barrier_async
dispatch_barrier_async(concurrentQueue, ^(){
NSLog(@"dispatch-barrier");
});

dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-a");
});
dispatch_async(concurrentQueue, ^(){
NSLog(@"dispatch-b");
});

dispatch_group_async & dispatch_group_notify

1
2
3
4
5
6
7
dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);

dispatch_group_notify(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);

dispatch_group_async是创建 GCD 的 group 操作。在dispatchGroup中所有任务运行完之后,才会调用dispatch_group_notify,与代码的顺序无关。

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
dispatch_queue_t dispatchQueue = dispatch_queue_create("tbd.tech", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t dispatchGroup = dispatch_group_create();

dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-1 : %@", [NSThread currentThread]);
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-2 : %@", [NSThread currentThread]);
});

// dispatch_group_notify
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"Success : %@", [NSThread currentThread]);
});

dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-3 : %@", [NSThread currentThread]);
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-4 : %@", [NSThread currentThread]);
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-5 : %@", [NSThread currentThread]);
});
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
NSLog(@"dispatch-6 : %@", [NSThread currentThread]);
});

dispatch_apply

1
2
dispatch_apply(size_t iterations, dispatch_queue_t queue,
DISPATCH_NOESCAPE void (^block)(size_t));

dispathc_applydispatch_syncdispatch_group的关联 API.它以指定的次数将指定的 Block 加入到指定的队列中。并等待队列中操作全部完成.

1
2
3
4
5
6
7
8
9
10
11
12
13
NSArray *array = @[@"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10",
@"11",@"12",@"13",@"14",@"15",@"16",@"17",@"18",@"19",@"20"];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
dispatch_apply(array.count, queue, ^(size_t index){
//字典转模型
NSLog(@"%@", array[index]);
});

dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"处理完成,回到主线程。");
});
});

dispatch_semaphore & @synchronized

要防止对同一数据产生写冲突,就需要设置加锁(@synchronized)或者采用信号量(dispatch_semaphore)的方式。

dispatch_semaphore

1
2
3
4
5
6
// 创建
dispatch_semaphore_t dispatch_semaphore_create(long value);
// wait (-1)
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
// signal (+1)
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
NSMutableArray *array = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSDate *date = [[NSDate alloc] init];
for (int index = 0; index < 100000; ++index) {
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[array addObject:[NSNumber numberWithInt:index]];
dispatch_semaphore_signal(semaphore);
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"%lu", (unsigned long)array.count);
double time = [date timeIntervalSinceNow];
NSLog(@"time %f", time);
});

@synchronized

试验了一下性能不如dispatch_semaphore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
NSMutableArray *array = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
NSDate *date = [[NSDate alloc] init];
for (int index = 0; index < 100000; ++index) {
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
@synchronized(array) {
[array addObject:[NSNumber numberWithInt:index]];
}
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"%lu", (unsigned long)array.count);
double time = [date timeIntervalSinceNow];
NSLog(@"time %f", time);
});