iOS 开发 多线程详解之GCD任务和队列

队列

GCD的队列可以分为2大类型 :

  • 串行队列(Serial Dispatch Queue)

    • 让任务一个接着一个有序的执行:不管队列里面放的是什么任务.一个任务执行完毕后,再执行下一个任务.
    • 同时只能调度一个任务执行.
  • 并发队列(Concurrent Dispatch Queue)

    • 可以让多个任务并发/同时执行.自动开启多个线程同时执行多个任务.
    • 同时可以调度多个任务执行
    • 并发队列的并发功能只有内部的任务是”异步”任务时,才有效.

任务

GCD中有2个用来执行任务的函数 :

  • 同步的方式执行任务 : 在当前线程中依次执行任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

queue:队列
block:任务

  • 异步的方式执行任务 : 新开线程,在新线程中执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

queue:队列
block:任务

串行队列

  • 以先进先出的方式,顺序调度队列中的任务执行
  • 无论队列中所指定的执行任务函数是同步还是异步,都会等待前一个任务执行完成后,再调度后面的任务
// 参数1 : 队列的标示符
// 参数2 : 队列的属性.决定了队列是串行的还是并行的.
dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_SERIAL);
  • 串行队列+同步任务
#pragma mark - 串行队列+同步任务
/*
    1. 没有开新线程
    2. 循环是顺序打印
    3. @"end"最后执行
 */
- (void)gcdDemo1
{
    // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_SERIAL);

    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

这里写图片描述

  • 串行队列+异步任务
#pragma mark - 串行队列+异步任务
/*
    1. 开一条新线程 : 因为队列是顺序调度任务,前一个任务执行完成以后才能调度后面的任务,开一条新线程就够了
    2. 循环是顺序打印
    3. @"end"不是在最后执行
 */
- (void)gcdDemo2
{
    // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_SERIAL);

    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

这里写图片描述

并发队列

  • 以先进先出的方式,并发调度队列中的任务执行
  • 如果当前调度的任务是同步执行的,会等待任务执行完成后,再调度后续的任务
  • 如果当前调度的任务是异步执行的,会调度多个线程同时执行多个任务.
// 参数1 : 队列的标示符
// 参数2 : 队列的属性.决定了队列是串行的还是并行的.
dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_CONCURRENT);
  • 并发队列+同步任务
#pragma mark - 并发队列+同步任务
/*
    1. 没有开新线程
    2. 循环顺序打印
    3. @"end"最后执行
 */
- (void)gcdDemo1
{
    // 并发队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_CONCURRENT);

    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

这里写图片描述

  • 并发队列+异步任务
#pragma mark - 并发队列+同步任务
/*
 1. 开启多条新线程
 2. 循环无序打印
 3. @"end"不是最后执行
 */
- (void)gcdDemo2
{
    // 并发队列
    dispatch_queue_t queue = dispatch_queue_create("zj", DISPATCH_QUEUE_CONCURRENT);

    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }

    NSLog(@"end");
}

这里写图片描述

主队列

  • 专门用来在主线程上调度任务的队列.
  • 不会开启新线程.
  • 以先进先出的方式,在主线程空闲时才会调度队列中的任务在主线程执行.
  • 如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度.
//主队列是负责在主线程调度任务的,会随着程序启动一起创建,只需要获取不用创建.
dispatch_queue_t queue = dispatch_get_main_queue();
  • 主队列+异步任务
#pragma mark - 主队列+异步任务
/*
    1. 执行顺序 : @"start"->@"end"->执行中...
    2. 没有开启新线程
 */
- (void)gcdDemo1
{
    // 主队列 : 程序一启动就创建好的,不需要创建
    dispatch_queue_t queue = dispatch_get_main_queue();

    NSLog(@"start");

    dispatch_async(queue, ^{
        NSLog(@"执行中...%@",[NSThread currentThread]);
    });

    NSLog(@"end");
}

主队列+同步任务=死锁

#pragma mark - 主队列+同步任务=死锁
//以先进先出的方式,在主线程空闲时才会调度队列中的任务在主线程执行.
//如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度.
/*
    1. 执行顺序 : @"start"   后面的任务被阻塞了
    2. 同步任务和主线程相互等待,造成线程死锁
 */
- (void)gcdDemo2
{
    dispatch_queue_t queue = dispatch_get_main_queue();

    NSLog(@"start");

    dispatch_sync(queue, ^{
        NSLog(@"执行中...%@",[NSThread currentThread]);
    });

    NSLog(@"end");
}

死锁解决办法

#pragma mark - 死锁解决办法
// 主队列中的同步任务放进子线程中,不使其阻塞主线程
- (void)gcdDemo3
{
    dispatch_async(dispatch_queue_create("ZJ", DISPATCH_QUEUE_CONCURRENT), ^{

        NSLog(@"start");

        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"执行中...%@",[NSThread currentThread]);
        });

        NSLog(@"end");
    });
}

全局队列

  • 全局队列又叫全局并发队列.
  • 是系统为了方便程序员开发提供的,其工作表现与并发队列一致
  • 全局队列
    • 没有名称
    • 无论 MRC & ARC 都不需要考虑释放
    • 日常开发中,建议使用”全局队列”
  • 并发队列
    • 有名字,和 NSThread 的 name 属性作用类似
    • 如果在 MRC 开发时,需要使用 dispatch_release(q); 释放相应的对象
    • 开发第三方框架时,建议使用并发队列

1.全局队列+异步任务

运行效果和并发队列一样
#pragma mark - 全局队列
// 执行效果跟并发队列一样的
- (void)gcdDemo
{
    // 全局队列,跟主队列一样不需要创建
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"%d %@",i,[NSThread currentThread]);
        });
    }
    NSLog(@"end");
}

队列和任务组合的总结

  • 同步和异步决定了要不要开启新的线程 (同步不开,异步开)

    • 同步:在当前线程中执行任务,不具备开启新线程的能力
    • 异步:在新的线程中执行任务,具备开启新线程的能力
  • 串行和并发决定了任务的执行方式

    • 串行:一个任务执行完毕后,再执行下一个任务
    • 并发:多个任务并发(同时)执行
  • 当任务是异步的时候,队列决定了开启多少条线程

    • 串行队列 : 只开一条
    • 并发队列 : 可以开启多条

这里写图片描述

同步任务的作用

当多个任务之间有依赖关系时,可以将任务定义成同步的.
需求 : 登陆->付费->下载->通知用户

使同步任务在子线程中建立依赖关系
- (void)GCDDemo2
{
    // 创建全局的并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    dispatch_async(queue, ^{

        dispatch_sync(queue, ^{
            // 查看当前线程
            NSLog(@"登陆 %@",[NSThread currentThread]);
        });

        dispatch_sync(queue, ^{
            // 查看当前线程
            NSLog(@"付费 %@",[NSThread currentThread]);
        });

        dispatch_sync(queue, ^{
            // 查看当前线程
            NSLog(@"下载 %@",[NSThread currentThread]);
        });

        dispatch_async(dispatch_get_main_queue(), ^{
            // 查看当前线程
            NSLog(@"通知用户 %@",[NSThread currentThread]);
        });
    });
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值