Day10-GCD 进阶

YVTU

一、GCD 队列底层原理

1.1 队列类型

  • 串行队列:一次只执行一个任务。
  • 并发队列:可以并发执行多个任务。
  • 主队列:主线程的串行队列,用于更新 UI。

1.2 队列背后的线程

  • GCD 使用 线程池 管理线程,具体由 libdispatch + pthread 实现。
  • 并发队列任务实际是串行调度、并发执行(由系统根据线程池和资源自动调度)。

二、常见 GCD 高级用法

2.1 dispatch_barrier(栅栏函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
dispatch_queue_t queue = dispatch_queue_create("com.example.queue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{
// 任务1
});

dispatch_barrier_async(queue, ^{
// 栅栏任务:写操作
});

dispatch_async(queue, ^{
// 任务2
});

2.2 dispatch_group(任务组)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

dispatch_group_async(group, queue, ^{
// 任务1
});

dispatch_group_async(group, queue, ^{
// 任务2
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 所有任务完成后执行
});

2.3 dispatch_semaphore(信号量)

1
2
3
4
5
6
7
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 临界区
dispatch_semaphore_signal(semaphore);
});

三、避免死锁与优雅使用

3.1 死锁示例(主队列同步调用)

1
2
3
dispatch_sync(dispatch_get_main_queue(), ^{
// 死锁:当前已经在主线程,又等主队列执行
});

3.2 dispatch_once(线程安全的单例初始化)

1
2
3
4
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 初始化逻辑
});

四、GCD 优化技巧与实践

4.1 控制最大并发数(结合 semaphore)

1
2
3
4
5
6
7
8
dispatch_semaphore_t sema = dispatch_semaphore_create(3);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
// 下载任务
dispatch_semaphore_signal(sema);
});
}

4.2 使用 QoS(服务质量)

1
2
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_USER_INITIATED, 0);
dispatch_queue_t queue = dispatch_queue_create("com.example.qosqueue", attr);

五、和 NSOperation 比较

特性 GCD NSOperation
简洁性 ❌(更复杂)
依赖管理 ✅(operation.addDependency)
取消任务 ✅(operation.cancel)
重用性/扩展性 ✅(子类化)
并发控制(数量) 借助 semaphore ✅(maxConcurrentOperationCount)

GCD 的底层实现原理

Grand Central Dispatch(GCD)是苹果提供的一个多线程并发编程框架,其底层实现原理可以从几个关键的技术点来理解,包括:


一、核心结构与实现基础

1. GCD 基于 libdispatch 实现

GCD 的底层是开源的 libdispatch 库,核心用 C 和 C++ 编写,运行在系统内核之上,封装了线程调度、任务分发、队列管理等功能。


二、任务与队列模型

1. 任务:Block 形式的封装代码(实质是闭包)

每个任务是一个 dispatch_block_t,本质是封装在结构体中的函数指针和上下文数据。

2. 队列:串行 or 并发

  • 串行队列:任务一个个顺序执行。
  • 并发队列:任务可同时执行,具体是否并行取决于系统资源。

底层由结构体 dispatch_queue_s 表示,内部包含:

  • 队列名称
  • 优先级(QoS)
  • Target Queue(用于继承行为)
  • 队列状态(是否正在执行任务)
  • 队列类型(串行/并发)

三、线程调度原理

1. 线程池机制

GCD 不直接创建线程,而是复用一个系统管理的线程池(Managed Thread Pool):

  • Apple 使用内核的 pthreadkqueue 机制动态创建和调度线程。
  • 调用 dispatch_async 等方法时任务进入队列,等待调度器(libdispatch)判断是否需要从线程池中取线程执行。

2. 结合内核的调度器

  • 使用 XNU 内核提供的 workloop(基于 kqueue)来监视队列变化。
  • 利用信号量、原子操作、互斥锁控制任务同步。

四、队列的调度机制

1. dispatch_async / dispatch_sync 的区别

方法 调度行为 是否阻塞线程
dispatch_async 异步入队
dispatch_sync 同步执行,当前线程等待任务完成

底层通过:

  • dispatch_async:创建任务对象 -> 放入目标队列 -> 唤醒线程池中的线程处理
  • dispatch_sync:直接调用任务,并使用 semaphore 阻塞当前线程,直到任务执行完

五、QoS(服务质量)控制

GCD 支持 QoS(Quality of Service)等级,例如:

  • userInteractive(最高)
  • userInitiated
  • default
  • utility
  • background(最低)

这些优先级通过底层设置线程调度属性,在内核中会影响线程抢占和执行顺序。


六、关键数据结构(源码角度)

1. dispatch_queue_s(队列结构体)

包含:

1
2
3
4
5
6
7
struct dispatch_queue_s {
...
dispatch_object_t _head; // 队列头部
dispatch_object_t _tail; // 队列尾部
pthread_priority_t _priority; // 优先级
...
};

2. dispatch_continuation_s(封装 block 的任务结构体)

1
2
3
4
5
struct dispatch_continuation_s {
void *dc_func; // block 函数指针
void *dc_ctxt; // block 上下文
...
};

七、并发控制:信号量、栅栏、组

  • dispatch_semaphore:底层基于 semaphore_t(Mach 信号量)
  • dispatch_barrier:写操作时阻塞其他任务,底层使用任务依赖和标记机制实现
  • dispatch_group:通过计数器追踪任务组状态,配合信号量实现通知

总结

GCD 是基于 libdispatch 构建的线程池 + 队列调度框架,其核心优势包括:

  • 高效的线程复用
  • 简洁的 API 封装(block)
  • 动态优先级管理(QoS)
  • 底层结合内核机制(如 kqueue 和 pthread)