多线程(GCD)

多线程(GCD)

GCD的基本使用

  • GCD是苹果为多核并行运算提出的解决方案
  • GCD会自动利用更多的内核
  • GCD同NSOperation一样,也会自动管理线程的生命周期
  • GCD是C的代码,较NSOperation性能更高
  • GCD也是以队列的形式工作,FIFO
  • 基本用法
    //线程间通信
    //进入子线程进行耗时操作
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"%@",[NSThread currentThread]);
        //回主线程进行刷新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
    });

GCD的两种队列

  • 并发队列(ConcurrentQueue):可以并发执行多个任务,允许开启多个线程同时执行任务(只是允许,要根据是否具有开启新线程的能力),所以只有异步函数才会对并发队列起作用,
  • 串行队列(SerialQueue):让任务一个接着一个的执行,
    • GCD自带一个特殊的串行队列为主队列,主队列自动会放到主线程中执行
    • GCD还自带一个全局的并发队列,可以设置优先级(dispatch_get_global_queue)

同步和异步

  • 同步:只能在当前线程中执行任务,不具备开启新线程的能力
  • 异步:具备开启新线程的能力
同步 异步
串行 没有开启线程,串行执行任务 开启一条线程,串行执行任务
并发 没有开启线程,串行执行任务 开启线程(大于等于1条,不受控制),并发执行任务

各种函数队列演示代码

同步串行(打印结果12345)

//创建一个串行队列
    dispatch_queue_t queue = dispatch_queue_create("duilie", DISPATCH_QUEUE_SERIAL);
//创建一个并行队列
    dispatch_queue_t queeu = dispatch_queue_create("bbb", DISPATCH_QUEUE_CONCURRENT);
    //同步执行这个串行队列(顺序执行,不会创建新的线程)
    dispatch_sync(queue, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"4%@",[NSThread currentThread]);
    });
    NSLog(@"5%@",[NSThread currentThread]);

同步并行(打印结果12345)

    //同步执行一个并行队列(顺序执行,没有开启线程,只是允许并行执行,但是没有线程这个条件,实际还是串行执行)
    dispatch_sync(queeu, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_sync(queeu, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_sync(queeu, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
    dispatch_sync(queeu, ^{
        NSLog(@"4%@",[NSThread currentThread]);
    });
    NSLog(@"5%@",[NSThread currentThread]);

异步串行(打印结果1234,5不一定插在1234的哪个位置,因为1234和5是并行的,1234是串行的)

    //异步执行这个串行队列(1234顺序执行,5和1234并行,开启一条线程,)
    dispatch_async(queue, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"4%@",[NSThread currentThread]);
    });
    NSLog(@"5%@",[NSThread currentThread]);

异步并行(打印结果随机,12345五个任务执行都是并发)

    //异步执行一个并行队列(主线程和其他线程一起执行,顺序不一定,开启的线程数也不一定)
    dispatch_async(queeu, ^{
        NSLog(@"1%@",[NSThread currentThread]);
    });
    dispatch_async(queeu, ^{
        NSLog(@"2%@",[NSThread currentThread]);
    });
    dispatch_async(queeu, ^{
        NSLog(@"3%@",[NSThread currentThread]);
    });
    dispatch_async(queeu, ^{
        NSLog(@"4%@",[NSThread currentThread]);
    });
    NSLog(@"5%@",[NSThread currentThread]);

系统提供的五种队列(1主队列 + 4全局子队列)

    //1.当前队列的优先级2.没用
    /**
     * DISPATCH_QUEUE_PRIORITY_HIGH 2
     * DISPATCH_QUEUE_PRIORITY_DEFAULT 0
     * DISPATCH_QUEUE_PRIORITY_LOW (-2)
     * DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
     */
    //获取到全局子队列
    dispatch_queue_t qq = dispatch_get_global_queue(0, 0);
    //获取主队列
    dispatch_queue_t q =  dispatch_get_main_queue();

GCD的其他函数

  • 延时执行
 //延时执行
    //参数:几秒后执行(填的是2,代表两秒后执行)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延迟执行的代码片段");
    });
    NSLog(@"我先走了");
  • 只执行一次(线程安全,只有创建单例用它,其他地方不要用)
    //只执行一次(线程安全的)
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"只走一次");
    });
  • 重复执行
 //重复执行
    //1.重复的次数 2.在什么队列重复 3.当前次数的索引
    dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t a) {
        NSLog(@"%zu",a);
    }) ;
  • 线程组
    • 本质是先把任务放到队列中,再把队列放入组内
    • 优点:可以监听组内的任务完毕
//创建一个组(先把任务放进队列里,在放进组里,组可以监听任务完毕)
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务1");
    });
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务2");
    });
    //监听组内执行情况(监听组内所有队列执行完毕)
    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"执行完毕");
    });
  • 单独执行(dispatch_barrier_async)
    • 本质是在并发队列中拥有串行的权利
    • 如果有12345个任务,给3任务设置单独执行,按照12345的顺序添加任务,那么一定是12执行完才会执行3,3执行完才会执行45,所以12和45在执行的时候是并发,但是到了3,12和3和45其实是串行的
    • 注意:如果想要这样设置其执行顺序(其实是仿照NSOperation设置依赖),那么所在的队列一定不能是系统提供的全局并发队列
    //创建并发队列
    dispatch_queue_t tt = dispatch_queue_create("tt", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(tt, ^{
        NSLog(@"任务1");
    });
    dispatch_async(tt, ^{
        NSLog(@"任务2");
    });
    //单独执行
    dispatch_barrier_sync(tt, ^{
        NSLog(@"任务3");
    });
    dispatch_async(tt, ^{
        NSLog(@"任务4");
    });
    dispatch_async(tt, ^{
        NSLog(@"任务5");
    });
  • block变成函数执行
    • 本质是将block块变成c语言的函数,两种方式是一样的(dispatch_async和dispatch_async_f)
 dispatch_async_f(dispatch_get_global_queue(0, 0), @"1", sum);
- void sum (void *a)
{
    NSLog(@"%@",a);
}

资源抢夺

  • 再实际开发过程中会将常出现卖票效应,其实就是多个线程访问同一个实例变量,并改变实例变量的值,就会导致实例变量的值发生错误,
  • 互斥锁(@synchronized)有效的解决了这个问题
  • 互斥锁原理:使用了线程同步技术,多条线程在同一条线上执行,并且按顺序执行
  • 将要访问的实例变量套用在互斥锁里面
    int a = 1000;
    //资源抢夺(对象)
    //互斥锁带的参数一定要传一个对象,一般情况传self
    @synchronized(self) {
        //锁的是代码
        a = a - 1;
    }
  • 互斥锁的缺点:会消耗大量的CPU资源

你可能感兴趣的:(多线程(GCD))