GCD

Dispatch Queue

Serial Dispatch Queue:依次执行,等待现在执行中处理结束
Concurrent Dispatch Queue: 并发执行,不等待现在执行中处理结束

Dispatch Queue 种类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// serial dispatch queue
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.gcd.serialDispatchQueue", NULL)

// concurrent dispatch queue
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.gcd.concurrentDispatchQueue", DISPATCH_QUEUE_CONCURRENT)

// main queue(serial) queue
dispatch_queue_t mainDispatchQueue = dispatch_get_main_queue()

// global queue(concurrent) queue
/*
四种优先级:
DISPATCH_QUEUE_PRIORITY_HIGH 默认
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台优先级
*/
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)

dispatch_set_target_queue

dispatch_queue_create 生成的队列都是具有默认有优先级,因此变更队列的优先级使用dispatch_set_target_queue。将不可并行执行的处理追加到多个Serial Dispatch Queue中时,如果使用dispatch_set_target_queue函数指定为某一个Serial Dispatch Queue,则可以防止处理并行执行。

注意:第一个参数如果指定为Main Dispatch QueueGlobal Dispatch Queue则不知道会出现什么情况,因此这些均不可指定。

1
2
3
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.serial", NULL);
dispatch_queue_t globalDispatchQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_set_target_queue(serialDispatchQueue, globalDispatchQueue);

dispatch_after

使用dispatch_after延时将Block追加到Main Dispatch Queue中处理。

1
2
3
4
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"waited at least three seconds.");
});

Dispatch Group

在追加多个Dispatch Queue中的多个处理全部结束后想执行结束处理,使用Dispatch Group。

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 queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"blk0");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk1");
});
dispatch_group_async(group, queue, ^{
NSLog(@"blk2");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"done");
});

// blk1
// blk2
// blk3
// done

// Dispatch Group 处理尚未执行结束,就会一直等待
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

dispatch_barrier_async

使用dispatch_barrier_async来避免读写数据库导致的竞争。使用Concurrent Dispatch Queuedispatch_barrier_async可以实现高效率的数据库访问和文件访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
dispatch_queue_t queue = dispatch_queue_create("com.gcd.concureent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{NSLog(@"blk0_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk1_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk2_for_reading");});
dispatch_barrier_async(queue, ^{NSLog(@"blk2_for_writing");});
dispatch_async(queue, ^{NSLog(@"blk3_for_reading");});
dispatch_async(queue, ^{NSLog(@"blk4_for_reading");});

// output
// blk0_for_reading
// blk1_for_reading
// blk2_for_reading
// blk2_for_writing
// blk3_for_reading
// blk4_for_reading

dispatch_sync、dispatch_async

dispatch_async:将指定Block非同步追加到指定的Dispatch Queue中,dispatch_async函数不等待Block内的处理结束。
dispatch_sync:将指定Block同步追加到指定的Dispatch Queue中,在追加Block结束之前,dispatch_sync函数会一直等待Block内部的处理结束。

Main Dispatch Queue中谨慎使用dispatch_sync,这样会造成死锁:

Main Dispatch Queue等待Block执行结束,而主线程正在执行这些代码,dispatch_sync又在等待主队列任务结束才将Block添加进queue,所以无法将Block追加到Main Dispatch Queue当中,最终造成死锁。

1
2
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{ NSLog(@"Hello!"); });

同行以下也会造成死锁:

1
2
3
4
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
dispatch_sync(queue, ^{ NSLog(@"Hello!"); });
});
1
2
3
4
dispatch_queue_t queue = dispatch_queue_create("com.serialQueue", NULL);
dispatch_async(queue, ^{
dispatch_sync(queue, ^{ NSLog(@"Hello!"); });
});

dispatch_apply

dispatch_applydispatch_syncDispatch Group的关联API,该函数指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束。

注意:由于dispatch_applydispatch_sync相同,会等待处理执行结束,因此推荐在dispatch_async函数中非同步地执行dispatch_apply

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
NSArray *array = @[@0, @1, @2, @3, @4];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 非同步执行
dispatch_async(queue, ^{
// 同步执行,等待dispatch_apply全部处理结束
dispatch_apply([array count], queue, ^(size_t index) {
NSLog(@"%zu: %@", index, [array objectAtIndex:index]);
});

// dispatch_apply 执行结束,在 Main Dispatch Queue 中非同步执行
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"done");
});
});

// output
// 0: 0
// 1: 1
// 2: 2
// 3: 3
// 4: 4
// done

dispatch_suspend/dispatch_resume

dispatch_suspend(queue):挂起指定的 queue
dispatch_resume(queue):恢复指定的 queue

Disptach Semaphore

提供比 Serial Dispatch Queuedispatch_barrier_async 更加细粒度的控制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 生成 Dispatch Semaphore,并将计数器初始值设定为 1
// 保证可访问 NSMutableArray 类对象的线程同时只能有1个
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0; i < 10000; ++i) {
dispatch_async(queue, ^{
// 等待 Dispatch Semaphore,一直等待,直到 Dispatch Semaphore 的计数值达到大于等于 1
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// Dispatch Semaphore 的计数器大于等于 1,所以将 Dispatch Semaphore 的计数器减去 1,dispatch_semaphore_wait 函数执行返回
// 由于可访问 NSMutableArray 类对象的线程只有 1 个,因此可安全地进行更新
[array addObject:[NSNumber numberWithInt:i]];

// 排他控制结束,所以通过 dispatch_semaphore_signal 函数,将 Dispatch Semaphore 的计数器加 1
// 如果有通过 dispatch_semaphore_wait 函数,等待Dispatch Semaphore的计数值增加的线程,就由最先等待的线程执行
dispatch_semaphore_signal(semaphore);
});
}

dispatch_once

dispatch_once能够在使用多线程环境下,也可保证百分之百安全,这就是单例模式

1
2
3
4
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// ...
});

Dispatch I/O

如果想要提高文件读取速度,可以使用 Dispatch I/O

dispatch_io_read:使用 Global Dispatch Queue开始并列读取。
dispatch_io_set_low_water:设定第一次读取的大小(分割大小)。