如何取消GCD任务

第一种:dispatch_block_cancel

     iOS8之后可以调用dispatch_block_cancel来取消(需要注意必须用dispatch_block_create创建dispatch_block_t) 

     代码示例

- (void)gcdBlockCancel{
    
    dispatch_queue_t queue = dispatch_queue_create("com.gcdtest.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_block_t block1 = dispatch_block_create(0, ^{
        sleep(5);
        NSLog(@"block1 %@",[NSThread currentThread]);
    });
    
    dispatch_block_t block2 = dispatch_block_create(0, ^{
        NSLog(@"block2 %@",[NSThread currentThread]);
    });
    
    dispatch_block_t block3 = dispatch_block_create(0, ^{
        NSLog(@"block3 %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, block1);
    dispatch_async(queue, block2);
    dispatch_block_cancel(block3);
} 

打印结果:

2017-07-08 13:59:39.935 beck.wang[2796:284866] block2 <NSThread: 0x6180000758c0>{number = 3, name = (null)}
2017-07-08 13:59:44.940 beck.wang[2796:284889] block1 <NSThread: 0x618000074f80>{number = 4, name = (null)}
 

同样的,dispatch_block_cancel也只能取消尚未执行的任务,对正在执行的任务不起作用。

  第二种:定义外部变量,用于标记block是否需要取消
      该方法是模拟NSOperation,在执行block前先检查isCancelled = YES ?在block中及时的检测标记变量,当发现需要取消时,终止后续操作(如直接返回return)。 

- (void)gcdCancel{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    __block BOOL isCancel = NO;
    
    dispatch_async(queue, ^{
        NSLog(@"任务001 %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务002 %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任务003 %@",[NSThread currentThread]);
        isCancel = YES;
    });
    
    dispatch_async(queue, ^{
        // 模拟:线程等待3秒,确保任务003完成 isCancel=YES
        sleep(3);
        if(isCancel){
            NSLog(@"任务004已被取消 %@",[NSThread currentThread]);
        }else{
            NSLog(@"任务004 %@",[NSThread currentThread]);
        }
    });
} 

打印结果: 

2017-07-08 15:33:54.017 beck.wang[3022:333990] 任务002 <NSThread: 0x60800007f740>{number = 4, name = (null)}
2017-07-08 15:33:54.017 beck.wang[3022:333989] 任务001 <NSThread: 0x600000261d80>{number = 3, name = (null)}
2017-07-08 15:33:54.017 beck.wang[3022:333992] 任务003 <NSThread: 0x618000261800>{number = 5, name = (null)}
2017-07-08 15:34:02.266 beck.wang[3022:334006] 任务004已被取消 <NSThread: 0x608000267100>{number = 6, name = (null)} 

 

Grand Central Dispatch(GCD)是苹果提供的底层并发编程框架,用于高效地管理和调度任务。它基于 C 语言实现,封装在 `libdispatch` 库中,允许开发者通过简单的 API 将任务提交到合适的队列中,由系统自动管理线程的创建、复用和调度。 --- ## ✅ GCD 是如何管理任务的? GCD 的核心思想是:**将任务(work)从线程(thread)管理中解耦**,开发者不再手动创建或控制线程,而是把“要做的事”封装成**任务块(block 或函数)**,提交给 **调度队列(Dispatch Queue)**,由 GCD 内部的线程池统一调度执行。 --- ### 🧱 核心组件结构 | 组件 | 说明 | |------|------| | **任务(Task)** | 一段要执行的代码(block 或函数) | | **队列(Queue)** | 存放任务的 FIFO 队列(但执行顺序可受类型影响) | | **线程池(Thread Pool)** | GCD 维护的一组后台线程,动态调整数量 | | **调度器(Scheduler)** | 系统内核 + libdispatch 共同完成任务分发 | --- ## 🔁 任务提交与执行流程(简化版) ```swift DispatchQueue.global().async { // 这是一个任务(block) print("我在后台运行") } ``` 1. 开发者调用 `async` 方法,传入一个闭包(任务) 2. GCD 将该任务加入指定的 **全局并发队列** 3. GCD 的调度系统检测到有可用线程 4. 从线程池中取出一个线程执行该任务 5. 执行完毕后,线程归还线程池,等待下一个任务 > ⚠️ 注意:你不能假设任务运行在哪个具体线程上 —— 这是由系统决定的。 --- ## 📦 队列类型与任务调度方式 GCD 提供两种主要类型的队列,决定了任务如何被管理和执行: ### 1. **串行队列(Serial Queue)** - 每次只执行一个任务 - 任务按 FIFO 顺序逐个执行 - 适合保护临界资源、避免竞争 ```swift let myQueue = DispatchQueue(label: "com.example.serial") myQueue.async { print("任务1") } myQueue.async { print("任务2") } // 输出一定是: // 任务1 // 任务2 ``` ### 2. **并发队列(Concurrent Queue)** - 可同时执行多个任务 - 系统根据 CPU 核心数和负载动态分配线程执行 - 不保证完成顺序 ```swift let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent) concurrentQueue.async { print("并发任务1") } concurrentQueue.async { print("并发任务2") } // 输出顺序不确定,可能并行执行 ``` 此外还有几个**系统预定义队列**: | 队列 | 用途 | |------|------| | `DispatchQueue.main` | 主队列,串行执行,用于更新 UI | | `DispatchQueue.global(qos: .userInitiated)` | 全局并发队列,按 QoS 分级 | --- ## ⚙️ GCD 内部是如何工作的?(高级原理) ### 1. **基于线程池的动态调度** - GCD 不会为每个任务创建新线程(避免 `pthread_create` 开销) - 它维护一个**全局线程池**,包含若干个内核级线程 - 当任务被提交到并发队列时,GCD 从中选取空闲线程执行任务 - 任务完成后,线程返回池中复用 👉 极大减少了线程创建/销毁的开销,提升性能。 --- ### 2. **层级队列模型(Hierarchical Queues)** GCD 支持队列嵌套依赖关系: ```swift let queue = DispatchQueue(label: "com.example.work") let barrierQueue = DispatchQueue(label: "barrier", attributes: .concurrent) // 使用 barrier 实现读写锁 barrierQueue.async(flags: .barrier) { // 写操作(独占) } barrierQueue.async { // 读操作(可并发) } ``` - `.barrier` 标志的任务会阻塞后续任务,直到它完成 - 实现了高效的“并发读 + 独占写”模式 --- ### 3. **QoS(服务质量)驱动调度** GCD 支持为任务设置优先级(Quality of Service),帮助系统决定执行顺序和资源分配。 ```swift DispatchQueue.global(qos: .userInitiated).async { // 用户触发的重要任务(如加载数据) } DispatchQueue.global(qos: .utility).async { // 后台任务(如下载、导入) } ``` | QoS 级别 | 场景 | |--------|------| | `.userInteractive` | UI 刷新、动画(高优先级) | | `.userInitiated` | 用户点击后需要响应的操作 | | `.utility` | 中等耗时任务(如解析文件) | | `.background` | 低优先级任务(如日志上传) | 💡 系统会根据 QoS 自动调整 CPU 调度、节能策略等。 --- ### 4. **任务同步与异步** | 方式 | 行为 | |------|------| | `.async` | 提交任务后立即返回,不等待执行结果 | | `.sync` | 阻塞当前线程,直到任务执行完成 | ⚠️ 危险示例(死锁): ```swift DispatchQueue.main.sync { // 在主线程调用 sync 到主队列 → 死锁! } ``` 因为主线程在等待 block 执行,而 block 又必须在主线程执行 → 形成循环等待。 --- ## 🧩 实际例子:GCD 如何管理多个任务 ```swift print("开始") // 并发队列 let concurrent = DispatchQueue(label: "conc", attributes: .concurrent) concurrent.async { print("A1") } concurrent.async { print("A2") } concurrent.async { print("A3") } print("结束") ``` 输出可能是: ``` 开始 结束 A1 A3 A2 ``` ✅ 说明: - `async` 不阻塞主线程 - 多个任务可以并发执行 - 输出顺序不可预测 --- ## ✅ 总结:GCD 管理任务的核心机制 | 特性 | 描述 | |------|------| | **抽象线程** | 开发者只需关注“做什么”,而非“在哪做” | | **自动调度** | 系统根据 CPU、内存、QoS 动态调度任务 | | **队列模型** | FIFO 提交,串行/并发语义清晰 | | **线程复用** | 使用线程池避免频繁创建线程 | | **安全机制** | 支持 barrier、group、semaphore 等同步工具 | | **性能优化** | 减少上下文切换,提高缓存命中率 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值