资讯专栏INFORMATION COLUMN

GCD 学习总结

animabear / 2539人阅读

摘要:中两个非常重要的概念任务和队列任务分为同步执行和异步执行同步和异步的区别在于是否会阻塞当前线程其实在中一个任务就是一个中的代码队列分为串行队列和并行队列主队列是串行队列我们可以使用来创建新的队列串行队列并行队列下面是我自己总结的同步执行异步

GCD

GCD中两个非常重要的概念: 任务队列

任务分为同步执行sync异步执行async, 同步和异步的区别在于是否会阻塞当前线程, 其实在GCD中一个任务就是一个block中的代码.

队列分为串行队列并行队列,主队列dispatch_get_main_queue( )是串行队列.

我们可以使用dispatch_queue_create来创建新的队列,

//串行队列
  dispatch_queue_t queue = dispatch_queue_create("testQueue", NULL);
  dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL);
  //并行队列
  dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);

下面是我自己总结的:

/ 同步执行sync 异步执行async
串行队列 当前线程,一个一个执行 其他一个线程, 一个一个执行
并行队列 当前线程,一个一个执行 其他一个或者多个线程(取决于任务数), 同时执行

通过代码来验证一下:

创建一个串行的队列添加4个同步执行的任务

//串行队列
  dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL); 
  dispatch_sync(queue, ^{
      NSLog(@"111");
      NSLog(@"111中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"222");
      NSLog(@"222中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"333");
      NSLog(@"333中 %@",[NSThread currentThread]);
  });
  dispatch_sync(queue, ^{
      NSLog(@"444");
      NSLog(@"444中 %@",[NSThread currentThread]);
  });

打印结果:

2018-03-09 15:34:26.474786+0800 Learning[3385:281473] 111
2018-03-09 15:34:26.475053+0800 Learning[3385:281473] 111中 {number = 1, name = main}
2018-03-09 15:34:26.475165+0800 Learning[3385:281473] 222
2018-03-09 15:34:26.475369+0800 Learning[3385:281473] 222中 {number = 1, name = main}
2018-03-09 15:34:26.475568+0800 Learning[3385:281473] 333
2018-03-09 15:34:26.476057+0800 Learning[3385:281473] 333中 {number = 1, name = main}
2018-03-09 15:34:26.476249+0800 Learning[3385:281473] 444
2018-03-09 15:34:26.476351+0800 Learning[3385:281473] 444中 {number = 1, name = main}

同步dispatch_sync改为异步dispatch_async
打印结果依然是有序, 但是开启了一个子线程

2018-03-09 16:23:47.634329+0800 Learning[3830:331376] 111
2018-03-09 16:23:47.634739+0800 Learning[3830:331376] 111中 {number = 4, name = (null)}
2018-03-09 16:23:47.634911+0800 Learning[3830:331376] 222
2018-03-09 16:23:47.635262+0800 Learning[3830:331376] 222中 {number = 4, name = (null)}
2018-03-09 16:23:47.636508+0800 Learning[3830:331376] 333
2018-03-09 16:23:47.637206+0800 Learning[3830:331376] 333中 {number = 4, name = (null)}
2018-03-09 16:23:47.637413+0800 Learning[3830:331376] 444
2018-03-09 16:23:47.637680+0800 Learning[3830:331376] 444中 {number = 4, name = (null)}

如果把新建的队列改为并行队列, 同步执行

//并行队列
 dispatch_queue_t queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_CONCURRENT);
 dispatch_sync(queue, ^{
    NSLog(@"111");
    NSLog(@"111中 %@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"222");
    NSLog(@"222中 %@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"333");
    NSLog(@"333中 %@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
    NSLog(@"444");
    NSLog(@"444中 %@",[NSThread currentThread]);
});

结果

 2018-03-09 16:31:50.498599+0800 Learning[3938:341075] 111
 2018-03-09 16:31:50.498836+0800 Learning[3938:341075] 111中 {number = 1, name = main}
 2018-03-09 16:31:50.498946+0800 Learning[3938:341075] 222
 2018-03-09 16:31:50.499101+0800 Learning[3938:341075] 222中 {number = 1, name = main}
 2018-03-09 16:31:50.499227+0800 Learning[3938:341075] 333
 2018-03-09 16:31:50.499353+0800 Learning[3938:341075] 333中 {number = 1, name = main}
 2018-03-09 16:31:50.499461+0800 Learning[3938:341075] 444
 2018-03-09 16:31:50.499609+0800 Learning[3938:341075] 444中 {number = 1, name = main}

把上面代码中的同步改为异步, 即并行队列, 异步执行

2018-03-09 16:34:13.089611+0800 Learning[3982:344351] 222
2018-03-09 16:34:13.089612+0800 Learning[3982:344350] 333
2018-03-09 16:34:13.089612+0800 Learning[3982:344349] 111
2018-03-09 16:34:13.089639+0800 Learning[3982:344352] 444
2018-03-09 16:34:13.089997+0800 Learning[3982:344350] 333中 {number = 3, name = (null)}
2018-03-09 16:34:13.089997+0800 Learning[3982:344349] 111中 {number = 4, name = (null)}
2018-03-09 16:34:13.090004+0800 Learning[3982:344352] 444中 {number = 6, name = (null)}
2018-03-09 16:34:13.090031+0800 Learning[3982:344351] 222中 {number = 5, name = (null)}

从结果中可以看得出, 开启了四个不同的现成来执行四个任务.


注意
在同步+串行的时候会有一个特殊的情况, 上面也提到了, 主队列也是一个串行队列, 如果当前在主线程中且把任务加到主队列中会如何呢 ?

NSLog(@"---前 %@",[NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{//dispatch_sync
    NSLog(@"---中 %@",[NSThread currentThread]);
});
NSLog(@"---后 %@",[NSThread currentThread]);

运行结果形成死锁.
思考: 同样都是串行队列, 为什么任务添加到主队列会造成死锁现象, 新建一个串行队列则可以正常执行 ?
解释: 在执行完第一个NSLog的时候, 遇到dispatch_sync, 会暂时阻塞主线程. 那么我们想一下, 在阻塞之前, 主线程正在执行的是主队列中的哪一个任务呢? 用我们实际的例子来解释, 我们再写上面的代码的时候, 这部分的代码是包裹在一个另方法中的, 就像我自己写的demo, 这段代码就写在viewDidLoad里面, 因为viewDidLoad这些任务也是添加在主队列中的, 所以说阻塞主线程时, 主线程应该是处理的主队中 viewDidLoad这个任务.
我们调用了dispatch_sync后向主队列中添加了新的任务, 也就是dispatch_sync后面block中的代码. 但是根据队列的FIFO规则, 新添加的block中的任务, 肯定是要排在主队列的最后.
因为是使用的dispatch_sync同步, 所以说必须要等执行dispatch_sync的block中的代码才会返回, 才会继续执行ViewDidLoad中dispatch_sync这个方法下面的方法, 也就是代码中的第三个NSLog, 但是block中的任务被放在了主队列的底部, 他是不可能在viewDidLoad这个任务还没完成的时候就执行到的, 所以就形成了两个任务互相等待的情况, 也就是形成了死锁.
所以说这样看来,GCD形成死锁的原因应该是是队列阻塞,而不是线程阻塞.

dispatch_group

解决先执行A和B, AB都执行完再执行C的这种情况.

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
dispatch_group_async(group, queue, ^{
    // 1
    sleep(5);
    NSLog(@"task 1");
});

dispatch_group_async(group, queue, ^{
    //2
    NSLog(@"task 2");
});

dispatch_group_notify(group, queue, ^{
        NSLog(@"done ");
    });

或者

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

dispatch_group_enter(group);
dispatch_async(queue, ^{
    NSLog(@"11");
    dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
    sleep(7);
    NSLog(@"22");
dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_async(queue, ^{
    NSLog(@"33");
    dispatch_group_leave(group);
});

dispatch_group_notify(group, queue, ^{
    NSLog(@"done");
});

结果都是先执行完上面的在打印这个done, 这个不需要解释了.

有时候可能会碰到dispatch_group_wait, 注意一下, 因为他会阻塞线程, 所以不能放在主线程里面执行, 等待的时间是以group中任务开始执行的时间算起.

dispatch_barrier_async

多线程最常见的问题就是读写,比如数据库读写,文件读写,读取是共享的,写是互斥.

使用Concurrent Dispatch Queue和dispatch_barrier_basync函数可实现高效率的数据库访问和文件访问。
如果允许多个线程进行读操作,当写文件时,阻止队列中所有其他的线程进入,直到文件写完成.

在读取文件时, 直接使用dispatch_async异步读取, 当要写文件是, 使用dispatch_barrier_async. 用代码看一下dispatch_async和dispatch_barrier_async的执行顺序.

 dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"task 1");
    });

    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"task 2");
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier -- ");
        sleep(3);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task 3");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task 4");
    });
    
    NSLog(@" end ");

结果是先打印完task1和2, 在打印完barrier, 最后打印task3和4, 同理到文件读写中, 文件读取直接用dispatch_async, 写入使用dispatch_barrier_async, 则在写入文件时, 不管前面有多少操作都会等待前面的操作完成, 而且在写入的时候, 也没有其他线程访问, 从而达到线程安全.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/17059.html

相关文章

  • IOS学习总结(1)——关于并发

    摘要:我就个人感受而言,总结一下并发编程以及相关的一些知识。实际上,所有的并发编程都是构建于线程之上的包括和操作队列。当方法执行结束后,运行时会特地清理并弃掉线程。 本文仅作为个人学习总结记录使用!能力有限,难免会有疏漏和错误,还望指出。共同进步。 独白 工作学习IOS已经有半年了,一直都想抽出时间记录一下自己的学习和工作经验,但都写到一半,就没有然后了。关于并发编程,其实国外国内已经有...

    summerpxy 评论0 收藏0
  • iOS多线程总结

    摘要:多线程总结多线程对比每个对象对应一个线程,真正最原始的线程。自带线程管理的抽象类。选择小结简单而安全的选择实现多线程即可。在频繁使用多线程的程序中一般不建议使用是中提供的对线程的封装。主队列运行在主线程之上,而自定义队列在后台执行。 iOS多线程总结 1、iOS多线程对比 1.NSThread每个NSThread对象对应一个线程,真正最原始的线程。1)优点:NSThread 轻量级最低...

    zhunjiee 评论0 收藏0
  • iOS进阶学习

    摘要:小试推出的是今年新推出面向开发者的机器学习框架。精简的网络封装不依赖第三方库精简的网络封装不依赖第三方库多线程开发之是苹果提供的一套多线程解决方案。 iOS 内存探秘 本文深入浅出的介绍了 iOS 系统的内存机制以及开发者所需要注意的问题 GMTC 上分享滴滴出行 iOS 端瘦身实践的 Slides 滴滴出行 iOS 端瘦身实践 iOS之widget开发(Today Extension...

    187J3X1 评论0 收藏0
  • GCD和NSOperation总结

    摘要:和的区别是一种轻量级的方法来实现多线程。控制起来比较麻烦,比如取消和暂停一个线程。例一共有四个优先级,,,。设置回调方法,当操作结束后,会调用设置的回调。这个会在主线程中执行。 GCD和NSOperation的区别 1、GCD是一种轻量级的方法来实现多线程。控制起来比较麻烦,比如取消和暂停一个线程。 2、NSOperation和NSOperationQueue相对于GCD效率上要低一点...

    用户84 评论0 收藏0
  • 五种最大公约数Python求解总结

      小编写这篇文章的主要目的,主要是给大家讲解一下,关于最大公约数的求解方法,下面小编集中给大家总结一下,具体操作的五种方法。  方法一:短除法  短除法是求最大公因数的一种方法,也可用来求最小公倍数。求几个数最大公因数的方法,开始时用观察比较的方法,即:先把每个数的因数找出来,然后再找出公因数,最后在公因数中找出最大公因数。后来,使用分解质因数法来分别分解两个数的因数,再进行运算。之后又演变为短...

    89542767 评论0 收藏0

发表评论

0条评论

animabear

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<