OC底层-GCD知识总结

1,239 阅读8分钟

闲谈

本文主要总结概括一下实际工作中多线程,以及自己对多线程的理解

多线程使用方式

  • Thread
  • NSOperation
  • GCD
  • 、、、 本文主要总结GCD的一些使用方法。

GCD简介

- Grand Central Dispatch(GCD)是异步执行任务的技术之一。讲用用程序中记述的线程管理使用代码在系统中实现。由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就更有效率。
- GCD主要有任务和队列构成

GCD中的概念

概念API理解
队列Dispatch Queue线程队列,类似于任务池,将定义好的任务添加进入队列即可按照一定规则执行(并发Concurrent、串行Serial)
异步线程dispatch_async
同步线程dispatch_sync将任务追加到指定的队列中,追加的Block结束之前,dispatch_sync函数会一直等待。
栅栏函数diapatch_barrier_async将任务追加到指定的队列中,dispatch_sync函数不会做任何等待。
一次性函数dispatch_once添加的Block任务在整个程序生命周期中仅仅执行一次
线程组Dispatch Group
信号量Dispatch Semaphone

DCG使用方法

  • 1.创建一个队列(串行队列、并发队列)
  • 2.创建任务,将任务追加到队列中,系统根据任务类型执行任务(同步、异步)

队列创建

  • 串行队列(Serial Dispatch Queue)
    • 每次只有一个任务被执行。让任务一个接着一个地执行。(只开启一个线程,一个任务执行完毕后,再执行下一个任务)
  • 并发队列(Concurrent Dispatch Queue)
    • 可以让多个任务并发执行。可以开启多个线程,同时执行多个任务
//串行队列
dispatch_queue_t queue = dispatch_queue_create("com.queue.serial", DISPATCH_QUEUE_SERIAL);
//并发队列
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);

任务创建

  • 异步
dispatch_async(queue, ^{
       // 异步任务
    });
  • 同步
dispatch_sync(queue, ^{
       // 同步任务
    });

由于有两种任务执行方式,以及两种队列方式,所以会有多种组合方式;

任务和队列的组合方式

并发队列串行队列主队列
同步(sync)不开启新线程,串行执行任务不开启新线程,串行执行任务死锁,任务等待主线程执行,但是是同步,主线程等待任务执行,造成死锁
异步(async)开启新线程,并发执行任务开启一条新线程,串行执行任务不开启新线程,串行执行任务

GCD具体实现

  • 串行队列+异步任务
dispatch_queue_t queue = dispatch_queue_create("com.queue.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"任务1--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3--%@",[NSThread currentThread]);
    });
    NSLog(@"任务4--%@",[NSThread currentThread]);

输出结果:

objc[1491:2645283] 任务4--<NSThread: 0x281490dc0>{number = 1, name = main}
objc[1491:2645294] 任务1--<NSThread: 0x2814c5680>{number = 5, name = (null)}
objc[1491:2645294] 任务2--<NSThread: 0x2814c5680>{number = 5, name = (null)}
objc[1491:2645294] 任务3--<NSThread: 0x2814c5680>{number = 5, name = (null)}
  • 串行队列+同步任务
dispatch_queue_t queue = dispatch_queue_create("com.queue.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"任务1--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务2--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务3--%@",[NSThread currentThread]);
    });
    NSLog(@"任务4--%@",[NSThread currentThread]);

输出结果:

 objc[1489:2644861] 任务1--<NSThread: 0x280c28dc0>{number = 1, name = main}
 objc[1489:2644861] 任务2--<NSThread: 0x280c28dc0>{number = 1, name = main}
 objc[1489:2644861] 任务3--<NSThread: 0x280c28dc0>{number = 1, name = main}
 objc[1489:2644861] 任务4--<NSThread: 0x280c28dc0>{number = 1, name = main}
  • 并发队列+异步执行
ispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
   dispatch_async(queue, ^{
      // 异步串行队列
        NSLog(@"任务1--%@",[NSThread currentThread]);
   });
   dispatch_async(queue, ^{
      // 异步串行队列
       NSLog(@"任务2--%@",[NSThread currentThread]);
   });
   dispatch_async(queue, ^{
      // 异步串行队列
       NSLog(@"任务3--%@",[NSThread currentThread]);
   });
   NSLog(@"任务4--%@",[NSThread currentThread]);

输出结果:

objc[1497:2646937] 任务4--<NSThread: 0x283e80dc0>{number = 1, name = main}
objc[1497:2646950] 任务2--<NSThread: 0x283ede540>{number = 5, name = (null)}
objc[1497:2646950] 任务3--<NSThread: 0x283ede540>{number = 5, name = (null)}
objc[1497:2646949] 任务1--<NSThread: 0x283ed1e80>{number = 4, name = (null)}
  • 并发队列+ 同步任务
dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
   dispatch_sync(queue, ^{
       NSLog(@"任务1--%@",[NSThread currentThread]);
   });
   dispatch_sync(queue, ^{
       NSLog(@"任务2--%@",[NSThread currentThread]);
   });
   dispatch_sync(queue, ^{
       NSLog(@"任务3--%@",[NSThread currentThread]);
   });
   NSLog(@"任务4--%@",[NSThread currentThread]);

输出结果

objc[1501:2647457] 任务1--<NSThread: 0x2811d4dc0>{number = 1, name = main}
objc[1501:2647457] 任务2--<NSThread: 0x2811d4dc0>{number = 1, name = main}
objc[1501:2647457] 任务3--<NSThread: 0x2811d4dc0>{number = 1, name = main}
objc[1501:2647457] 任务4--<NSThread: 0x2811d4dc0>{number = 1, name = main}
  • 主队列+异步任务
dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
       // 异步串行队列
         NSLog(@"任务1--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
       // 异步串行队列
        NSLog(@"任务2--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
       // 异步串行队列
        NSLog(@"任务3--%@",[NSThread currentThread]);
    });
    NSLog(@"任务4--%@",[NSThread currentThread]);

输出结果:

objc[1504:2648185] 任务4--<NSThread: 0x283544dc0>{number = 1, name = main}
objc[1504:2648185] 任务1--<NSThread: 0x283544dc0>{number = 1, name = main}
objc[1504:2648185] 任务2--<NSThread: 0x283544dc0>{number = 1, name = main}
objc[1504:2648185] 任务3--<NSThread: 0x283544dc0>{number = 1, name = main}
  • 主队列+同步任务
    • 程序崩溃,由于死锁程序无法执行
dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"任务1--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务2--%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"任务3--%@",[NSThread currentThread]);
    });
    NSLog(@"任务4--%@",[NSThread currentThread]);

栅栏函数

-设置栅栏拦截程序,队列中在栅栏之前的任务执行结束之前,栅栏后面的任务会一直等待

dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
         NSLog(@"任务1--%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2--%@",[NSThread currentThread]);
    });
    dispatch_barrier_async(queue, ^{
         for (int i = 0; i<4; i++) {
             NSLog(@"任务5--%@",[NSThread currentThread]);
         }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i<4; i++) {
            NSLog(@"任务3--%@",[NSThread currentThread]);
        }
    });
    NSLog(@"任务4--%@",[NSThread currentThread]);

输出结果:

objc[1517:2651721] 任务4--<NSThread: 0x283a74dc0>{number = 1, name = main}
objc[1517:2651733] 任务1--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务2--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务5--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}
objc[1517:2651733] 任务3--<NSThread: 0x283a22880>{number = 4, name = (null)}

一次行函数

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //任务代码
    });

线程组

  • 线程组在实际中主要应用场景是:需要在多个异步操作完成之后进行特定操作
  • dispatch_group_notify 函数在所有 dispatch_group_async 任务执行完毕之后才会通知开始后续操作 场景示例:APP中后台加载多个图片完成之后,在进行图片合成,组合成一张新的图片
 dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"任务0--%@",[NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"任务1--%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"任务2--%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"任务3--%@",[NSThread currentThread]);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //等待前面耗时操作完成回到主线程
        [NSThread sleepForTimeInterval:1];
        NSLog(@"任务4--%@",[NSThread currentThread]);
    });
    NSLog(@"任务5--%@",[NSThread currentThread]);

输出结果

2020-11-20 11:26:45.652124+0800 objc[1527:2656709] 任务0--<NSThread: 0x2814e0dc0>{number = 1, name = main}
2020-11-20 11:26:45.652206+0800 objc[1527:2656709] 任务5--<NSThread: 0x2814e0dc0>{number = 1, name = main}
2020-11-20 11:26:46.657634+0800 objc[1527:2656718] 任务2--<NSThread: 0x2814e7ac0>{number = 3, name = (null)}
2020-11-20 11:26:46.657748+0800 objc[1527:2656719] 任务1--<NSThread: 0x2814db800>{number = 5, name = (null)}
2020-11-20 11:26:46.657767+0800 objc[1527:2656720] 任务3--<NSThread: 0x2814a8040>{number = 6, name = (null)}
2020-11-20 11:26:47.659350+0800 objc[1527:2656709] 任务4--<NSThread: 0x2814e0dc0>{number = 1, name = main}
  • 线程组也可以使用 dispatch_group_enter、dispatch_group_leave完成
 dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"任务0--%@",[NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"任务1--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
         NSLog(@"任务2--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
         NSLog(@"任务3--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //等待前面耗时操作完成回到主线程
        [NSThread sleepForTimeInterval:1];
        NSLog(@"任务4--%@",[NSThread currentThread]);
    });
    NSLog(@"任务5--%@",[NSThread currentThread]);

输出结果

2020-11-20 11:34:55.314133+0800 objc[1530:2658637] 任务0--<NSThread: 0x280f78dc0>{number = 1, name = main}
2020-11-20 11:34:55.314234+0800 objc[1530:2658637] 任务5--<NSThread: 0x280f78dc0>{number = 1, name = main}
2020-11-20 11:34:56.319524+0800 objc[1530:2658651] 任务2--<NSThread: 0x280f29340>{number = 4, name = (null)}
2020-11-20 11:34:56.319513+0800 objc[1530:2658649] 任务1--<NSThread: 0x280f29540>{number = 3, name = (null)}
2020-11-20 11:34:56.319536+0800 objc[1530:2658650] 任务3--<NSThread: 0x280f34b80>{number = 6, name = (null)}
2020-11-20 11:34:57.321272+0800 objc[1530:2658637] 任务4--<NSThread: 0x280f78dc0>{number = 1, name = main}

信号量

  • 此函数理解为栅栏函数和串行队列的补充,更细粒度的控制
//初始化信号量
    dispatch_semaphore_t semaphonre = dispatch_semaphore_create(1);
    dispatch_queue_t queue = dispatch_queue_create("com.queue.concurrent", DISPATCH_QUEUE_CONCURRENT);
      NSLog(@"操作开始");
    for (int i = 1; i < 10; i ++) {
        dispatch_async(queue, ^{
           /*
            1.判断信号量是否>0
            2.只有信号量>0,后面代码才会继续执行
            3.DISPATCH_TIME_FOREVER 表示会一直等待
            */
            dispatch_semaphore_wait(semaphonre, DISPATCH_TIME_FOREVER);
            //此处同时只有一个线程进行访问,线程安全
            [NSThread sleepForTimeInterval:2];
            NSLog(@"操作===%d",i);
            /*
             1.执行完一次操作之后信号量+1
             2.信号量+1之后,原本dispatch_semaphore_wait处等待的操作按照先进先出原则进行执行
             */
            dispatch_semaphore_signal(semaphonre);
        });
    }
    NSLog(@"操作结束");

参考