iOS 底层 - 多线程-GCD

2020-04-06  本文已影响0人  水中的蓝天

本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !

队列

这里的队列指执行任务的等待队列,即用来存放任务的队列;

队列是一种特殊的线性表,一般开发中的队列有两种形式:串行队列、并发队列;

GCD

GCD源码

GCD的常用函数

用同步的方式执行任务

void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);

queue:队列
block:任务

用异步的方式执行任务

void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

每读取一个任务,则从队列中释放一个任务。队列的结构可参考下图:

串行队列.png
//创建串行队列
第一个参数:const char *_Nullable label  队列名 自定义就可以
第二个参数:dispatch_queue_attr_t _Nullable attr  队列标识 
DISPATCH_QUEUE_SERIAL:串行队列

dispatch_queue_create("mySerialqueu", DISPATCH_QUEUE_SERIAL);

注意:GCD创建串行队列,虽然使用了create方式创建;但这是不需要开发者自己释放的,GCD内部会进行处理。
CFUUIDCreate()这样的CoreFoundation框架中的C语言API不同,这是需要调用CFRelease(<#CFTypeRef cf#>)手动内存管理的.

-主队列

并发队列.png

//获取全局并发队列
DISPATCH_QUEUE_PRIORITY_HIGH           高               2
DISPATCH_QUEUE_PRIORITY_DEFAULT   默认==中               0
DISPATCH_QUEUE_PRIORITY_LOW            底              (-2)
DISPATCH_QUEUE_PRIORITY_BACKGROUND 后台即:最低级别  INT16_MIN

第一个参数:优先级 ,0 == DISPATCH_QUEUE_PRIORITY_DEFAULT
第二个参数:保留供将来使用的值-flags。传递任何非零的值都可能导致-->空返回值。所以最好传0进去
dispatch_get_global_queue(0, 0);


//创建并发队列
第一个参数:const char *_Nullable label  队列名 自定义就可以
第二个参数:dispatch_queue_attr_t _Nullable attr  队列标识 
DISPATCH_QUEUE_CONCURRENT :并发队列

dispatch_queue_create("myConcuQueu", DISPATCH_QUEUE_CONCURRENT);

容易混淆的术语

有4个术语比较容易混淆:同步异步并发串行

各种队列的执行效果

队列的执行效果@2x.png 死锁@2x.png
可以看出

问题1: 以下代码是在主线程执行,输出结果是什么 ? 为什么 ?

- (void)viewDidLoad {

    [super viewDidLoad];
    NSLog(@"任务1");
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        NSLog(@"任务2");
    });
    NSLog(@"任务3");
  }
  // dispatch_sync: 立马在当前线程执行任务, 执行完毕才会继续往下执行

输出结果:任务1

原因:

把问题1中的dispatch_sync函数改为dispatch_async函数呢 ?

输出结果:任务1、任务3、任务2
原因:dispatch_async不要求立马在当前线程同步执行任务,可以继续执行后面的代码

问题2: 举一个生活中的例子来说明线程死锁

小李和小明到自动取款机取钱;小明先到于是就开始插卡取钱了,这时候小李也到了也想取钱,小李呢脾气很火爆上来就要直接往卡槽里插卡取钱;
小明委屈的说:你让我先把卡退出来你在插卡呀。。。
小李一脸蛮横:哼!!! 我不管我要插卡取钱 !说话就拿卡往卡槽里塞
这时取款机老大爷怒了直接把俩人的卡全吞了 !END

问题3 :以下代码是在主线程执行输出结果是什么 ? 为什么 ?

- (void)interview
{

    NSLog(@"执行任务1");
  //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);

//异步开启子线程加入到串行队列中
    dispatch_async(queue, ^{ // 整一个dispatch_async函数调用代号:block0

        NSLog(@"执行任务2");
        
        dispatch_sync(queue, ^{ // dispatch_sync函数调用代号:block1
            NSLog(@"执行任务3");
        });
    
        NSLog(@"执行任务4");
    });
    
    NSLog(@"执行任务5");

}

输出结果:执行任务1、执行任务5、执行任务2 ---->死锁在dispatch_sync函数调用

原因:异步串行队列,串行队列是先进先出(FIFO),block0任务先被加入到队列中的,所以必须他先执行完才能执行后面加入的block1;
此时开启的同步任务执行block1,block0要执行完就需要等待block1执行完才能执行后面代码,block1需要等待block0执行完才能够开始执行;
这样就大家谁都没办法执行任务了 !直接阻塞了!

如果问题3中 dispatch_sync() 函数的任务队列是在另一个队列(串行队列、并行队列)中执行;结果如何 ?

答 :不会死锁了!因为不在同一个队列中。

问题4:以下代码是在主线程执行输出结果是什么 ? 为什么 ?

- (void)interview
{
    NSLog(@"执行任务1");
    //创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
  
  //把异步任务添加到并发队列中
    dispatch_async(queue, ^{ // block0

        NSLog(@"执行任务2");

       //把同步操作任务添加到并发队列中
        dispatch_sync(queue, ^{ // block1
            NSLog(@"执行任务3");
        });
        
        NSLog(@"执行任务4");
    });
    
    NSLog(@"执行任务5");
}

输出结果:执行任务1、执行任务5、执行任务2、执行任务3、执行任务4

原因: 并发队列支持同时执行多个任务,现在往并发队列中添加了两个任务:异步任务、同步任务;他们是不需要等另一个执行完再执行的,执行异步任务的同时也可以执行同步任务;但由于block0是异步所以执行到block1会先让他执行,之后再执行后面的代码。

问题5: 手动创建的全局并发队列和直接获取的全局并发队列有什么区别 ?

上一篇 下一篇

猜你喜欢

热点阅读