johnhao
johnhao
发布于 2017-05-06 / 5 阅读
0

iOS-多线程GCD

在 iOS 开发中,多线程是提升应用性能、优化用户体验的关键技术。而 GCD(Grand Central Dispatch) 是 Apple 提供的一套强大、高效的并发编程接口,它基于 C 语言实现,封装了底层线程管理的复杂性,让开发者可以专注于任务本身。

本文将系统讲解 GCD 的核心概念、使用方式、常见组合场景以及高级用法,并辅以代码示例,助你彻底掌握 GCD。


一、什么是 GCD?

GCD(Grand Central Dispatch) 是 Apple 在 Mac OS X 10.6(Snow Leopard)和 iOS 4 中引入的多核并发编程框架。其主要特点包括:

  • 自动利用多核 CPU 资源;

  • 基于“队列”模型管理任务;

  • 自动管理线程生命周期(创建、调度、销毁);

  • 使用 Block(闭包)作为任务单元,语法简洁;

  • 底层由系统维护线程池,高效且安全。

一句话总结:GCD 让你只需关心“做什么”,而不用操心“怎么做线程”。


二、GCD 的两个核心概念

1. 任务(Task)

任务是你希望在线程中执行的一段代码,通常以 Block 形式传递:

^{ 
    // 执行代码 
}

任务有两种提交方式:

方式

函数

是否阻塞当前线程

是否开启新线程

同步

dispatch_sync

✅ 是

❌ 否

异步

dispatch_async

❌ 否

✅ 可能(取决于队列类型)

⚠️ 注意:dispatch_async 不一定开启新线程!例如向主队列异步提交任务,仍会在主线程执行。


2. 队列(Dispatch Queue)

队列是任务的容器,遵循 FIFO(先进先出) 原则。GCD 提供两类队列:

(1)串行队列(Serial Queue)

  • 每次只执行一个任务;

  • 任务按顺序依次执行;

  • 默认只使用一个线程(除非是主队列)。

dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);

(2)并发队列(Concurrent Queue)

  • 可同时执行多个任务;

  • 系统根据资源自动分配多个线程;

  • 仅在异步提交时才并发执行

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);

特殊队列:

  • 主队列(Main Queue)

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    • 串行队列;

    • 所有任务在 主线程 执行;

    • UI 更新必须在此队列进行

  • 全局并发队列(Global Queue)

    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    • 系统提供的并发队列;

    • 支持不同优先级(如 HIGH, DEFAULT, LOW, BACKGROUND)。


三、GCD 的六种基本组合

执行方式 \ 队列类型

并发队列

串行队列

主队列

同步(sync)

当前线程,串行执行

当前线程,串行执行

主线程调用 → 死锁!
其他线程调用 → 串行执行

异步(async)

新线程,并发执行

新线程(1个),串行执行

主线程,串行执行

🔥 重点警告
dispatch_sync(dispatch_get_main_queue(), ^{...}) 在主线程中调用会导致 死锁
因为 sync 会等待任务完成,而主队列任务需等当前 runloop 结束才能执行,形成循环等待。


四、代码示例

1. 异步并发执行(常用后台任务)

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
    // 耗时操作:网络请求、文件读写等
    NSLog(@"后台线程:%@", [NSThread currentThread]);
    
    dispatch_async(dispatch_get_main_queue(), ^{
        // 回到主线程更新 UI
        self.label.text = @"加载完成";
    });
});

2. 串行队列保证顺序执行

dispatch_queue_t serial = dispatch_queue_create("com.example.download", DISPATCH_QUEUE_SERIAL);
for (int i = 0; i < 5; i++) {
    dispatch_async(serial, ^{
        NSLog(@"任务 %d - %@", i, [NSThread currentThread]);
        [NSThread sleepForTimeInterval:1];
    });
}
// 输出:0,1,2,3,4 依次打印,间隔1秒

3. 避免主队列同步死锁

❌ 错误写法(在主线程中):

dispatch_sync(dispatch_get_main_queue(), ^{ /* ... */ }); // 卡死!

✅ 正确做法:

dispatch_async(dispatch_get_main_queue(), ^{ /* UI 更新 */ });

五、GCD 高级用法

1. dispatch_once:线程安全的单例初始化

+ (instancetype)sharedInstance {
    static id instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

2. dispatch_after:延时执行

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
    NSLog(@"2秒后执行");
});

3. dispatch_group:任务组等待

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(), ^{
    NSLog(@"所有任务完成!");
});

4. dispatch_barrier_async:读写锁(用于自定义并发队列)

// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("com.example.barrier", DISPATCH_QUEUE_CONCURRENT);

// 读操作(并发)
dispatch_async(queue, ^{ /* 读取数据 */ });

// 写操作(屏障,等待所有读完成后再执行,执行时阻塞其他任务)
dispatch_barrier_async(queue, ^{
    // 安全写入
});

5. dispatch_semaphore:信号量控制并发数

dispatch_semaphore_t semaphore = dispatch_semaphore_create(3); // 最多3个并发
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        // 模拟耗时任务
        NSLog(@"执行任务 %d", i);
        sleep(2);
        dispatch_semaphore_signal(semaphore);
    });
}

六、最佳实践建议

  1. UI 操作永远在主队列

  2. 避免在主线程做耗时操作(防止卡顿);

  3. 慎用 dispatch_sync,尤其不要同步提交到当前队列;

  4. 优先使用全局队列,除非需要严格顺序(用串行队列);

  5. 善用 dispatch_groupdispatch_semaphore 控制复杂并发逻辑。


七、结语

GCD 是 iOS 并发编程的基石。掌握它不仅能写出高性能代码,还能避免常见的线程安全问题。希望本文能帮你建立起对 GCD 的系统认知。