美文网首页
整理自一篇gcd简书

整理自一篇gcd简书

作者: 叔叔不吃棒棒糖 | 来源:发表于2017-05-05 13:24 被阅读20次

一、一个异步、同步、并行、串行的表格

(第一个使用Markdown)
多核是gcd的基础

表格总结.png

二、任务:同步、异步、栅栏

同步:dispatch_sync
异步:dispatch_async
栅栏:dispatch_barrier_async
同步和异步我就不举🌰了
栅栏(zha lan)函数(我也不懂,查的百度)
和名字相同,栅栏的作用,等barrier之前的函数执行结束再执行barrier函数,之后的函数必须在barrier函数执行完毕之后才能运行,上代码吧
有栅栏函数

    dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"第一个异步任务%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第二个异步任务%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"栅栏函数%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第三个异步任务%@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"第四个异步任务%@",[NSThread currentThread]);
    });

运行结果

2017-05-05 13:22:46.847 retainCount[16150:1140225] 第二个异步任务<NSThread: 0x6000000688c0>{number = 5, name = (null)}
2017-05-05 13:22:46.847 retainCount[16150:1140226] 第一个异步任务<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.848 retainCount[16150:1140226] 栅栏函数<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.849 retainCount[16150:1140226] 第三个异步任务<NSThread: 0x6080000707c0>{number = 6, name = (null)}
2017-05-05 13:22:46.849 retainCount[16150:1140225] 第四个异步任务<NSThread: 0x6000000688c0>{number = 5, name = (null)}

一二任务被栅栏函数分割在上方,三四任务被分割在下方
有个规律,栅栏任务会在上一个任务的同一个线程开始,栅栏任务的下一个任务也会和栅栏任务在同一个线程开始(本人不知道原因)

注释掉栅栏函数之后运行结果

2017-05-05 13:24:26.448 retainCount[16183:1142163] 第四个异步任务<NSThread: 0x600000260980>{number = 6, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142160] 第三个异步任务<NSThread: 0x600000260740>{number = 5, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142180] 第一个异步任务<NSThread: 0x600000260940>{number = 3, name = (null)}
2017-05-05 13:24:26.448 retainCount[16183:1142161] 第二个异步任务<NSThread: 0x608000265ec0>{number = 4, name = (null)}

异步任务时间同时开始,在四个新开的线程中
【栅栏函数整理自http://www.cnblogs.com/denz/p/5277666.html

三、串行队列和并行队列

队列不想说太多了,之前有一篇已经说过一些了,再说可能会乱。但是看到一个总结感觉很好,写在这里

  • 同步异步,影响是否开启新的线程
  • 串行并行,影响任务的是否同时执行还是按顺序执行

四、GCD的使用

之前有写,但是感觉这个大牛写的更好,把代码又都写了一遍。

- (void)createUI
{
    UIButton *myBtn = [[UIButton alloc]initWithFrame:CGRectMake(20, 20, 300,50)];
    myBtn.backgroundColor = [UIColor redColor];
    [myBtn setTitle:@"子线程、同步任务、主队列" forState:UIControlStateNormal];
    
    [myBtn addTarget:self action:@selector(ChildSyncMain) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn];
    
    UIButton *myBtn1 = [[UIButton alloc]initWithFrame:CGRectMake(20, 90, 300,50)];
    myBtn1.backgroundColor = [UIColor redColor];
    [myBtn1 setTitle:@"主线程、同步任务、主队列" forState:UIControlStateNormal];
    [myBtn1 addTarget:self action:@selector(MainSyncMain) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn1];
    
    UIButton *myBtn2 = [[UIButton alloc]initWithFrame:CGRectMake(20, 160, 300,50)];
    myBtn2.backgroundColor = [UIColor redColor];
    [myBtn2 setTitle:@"主线程、异步任务、主队列" forState:UIControlStateNormal];
    
    [myBtn2 addTarget:self action:@selector(MainAsyncMain) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn2];
    
    UIButton *myBtn3 = [[UIButton alloc]initWithFrame:CGRectMake(20, 230, 300,50)];
    myBtn3.backgroundColor = [UIColor redColor];
    [myBtn3 setTitle:@"同步任务、并发队列" forState:UIControlStateNormal];
    
    [myBtn3 addTarget:self action:@selector(SyncConcurrent) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn3];
    
    UIButton *myBtn4 = [[UIButton alloc]initWithFrame:CGRectMake(20, 300, 300,50)];
    myBtn4.backgroundColor = [UIColor redColor];
    [myBtn4 setTitle:@"同步任务、串行队列" forState:UIControlStateNormal];
    
    [myBtn4 addTarget:self action:@selector(SyncSerial) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn4];
    
    UIButton *myBtn5 = [[UIButton alloc]initWithFrame:CGRectMake(20, 370, 300,50)];
    myBtn5.backgroundColor = [UIColor redColor];
    [myBtn5 setTitle:@"异步任务、串行队列" forState:UIControlStateNormal];
    
    [myBtn5 addTarget:self action:@selector(AsyncSerial) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn5];
    
    UIButton *myBtn6 = [[UIButton alloc]initWithFrame:CGRectMake(20, 440, 300,50)];
    myBtn6.backgroundColor = [UIColor redColor];
    [myBtn6 setTitle:@"异步任务、并行队列" forState:UIControlStateNormal];
    
    [myBtn6 addTarget:self action:@selector(AsyncConcurrent) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:myBtn6];
}

- (void)ChildSyncMain
{
    //第一个参数是优先级,第二个参数是保留字
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //开启一个子线程
    dispatch_async(queue, ^{
        //获取主队列
        dispatch_queue_t queue = dispatch_get_main_queue();
        //同步任务
        dispatch_sync(queue, ^{
            NSLog(@"任务所在线程%@",[NSThread currentThread]);
        });
    });
}

- (void)MainSyncMain
{
    //主队列,此时在主线程
    dispatch_queue_t queue = dispatch_get_main_queue();
    //同步任务
    dispatch_sync(queue, ^{
        NSLog(@"任务所在线程%@",[NSThread currentThread]);
    });
}

- (void)MainAsyncMain
{
    //与上一函数区别在于异步
    dispatch_queue_t queue = dispatch_get_main_queue();
    //异步任务
    dispatch_async(queue, ^{
        NSLog(@"任务所在线程%@",[NSThread currentThread]);
    });
}

- (void)SyncConcurrent
{
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //三个同步任务,放在并发队列,查看所在线程
    dispatch_sync(queue, ^{
        NSLog(@"1------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3------所在线程%@",[NSThread currentThread]);
    });
}

- (void)SyncSerial
{
    dispatch_queue_t queue = dispatch_queue_create("firstSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"1------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2------所在线程%@",[NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3------所在线程%@",[NSThread currentThread]);
    });
}

- (void)AsyncSerial
{
    
    dispatch_queue_t queue = dispatch_queue_create("SecondSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"1------所在线程%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2------所在线程%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3------所在线程%@",[NSThread currentThread]);
    });
}
//这个大牛写的太好了,不好意思抄了,直接copy过来吧。参数什么的解释的太好了
- (void)AsyncConcurrent
{
    /*
     执行任务
     dispatch_async
     dispatch_sync
     */
    
    /*
     第一个参数: 队列的名称
     第二个参数: 告诉系统需要创建一个并发队列还是串行队列
     DISPATCH_QUEUE_SERIAL :串行
     DISPATCH_QUEUE_CONCURRENT 并发
     */
    //    dispatch_queue_t queue = dispatch_queue_create("com.520it.lnj", DISPATCH_QUEUE_CONCURRENT);
    
    // 系统内部已经给我们提供好了一个现成的并发队列
    /*
     第一个参数: iOS8以前是优先级, iOS8以后是服务质量
     iOS8以前
     *  - DISPATCH_QUEUE_PRIORITY_HIGH          高优先级 2
     *  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      默认的优先级 0
     *  - DISPATCH_QUEUE_PRIORITY_LOW:          低优先级 -2
     *  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:
     
     iOS8以后
     *  - QOS_CLASS_USER_INTERACTIVE  0x21 用户交互(用户迫切想执行任务)
     *  - QOS_CLASS_USER_INITIATED    0x19 用户需要
     *  - QOS_CLASS_DEFAULT           0x15 默认
     *  - QOS_CLASS_UTILITY           0x11 工具(低优先级, 苹果推荐将耗时操作放到这种类型的队列中)
     *  - QOS_CLASS_BACKGROUND        0x09 后台
     *  - QOS_CLASS_UNSPECIFIED       0x00 没有设置
     
     第二个参数: 废物
     */
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    /*
     第一个参数: 用于存放任务的队列
     第二个参数: 任务(block)
     
     GCD从队列中取出任务, 遵循FIFO原则 , 先进先出
     输出的结果和苹果所说的原则不符合的原因: CPU可能会先调度其它的线程
     
     能够创建新线程的原因:
     我们是使用"异步"函数调用
     能够创建多个子线程的原因:
     我们的队列是并发队列
     */
    dispatch_async(queue, ^{
        NSLog(@"任务1  == %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务2  == %@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任务3  == %@", [NSThread currentThread]);
    });
}

这是个带按钮的操作,效果主要看打印,恩界面是这样的

gcd操作界面.png

打印是这样的(第二个按钮会造成死锁因此就没有打印)

2017-05-05 15:34:33.983 retainCount[17357:1229839] 任务所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:39.538 retainCount[17357:1229839] 任务所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.420 retainCount[17357:1229839] 1------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.421 retainCount[17357:1229839] 2------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:41.422 retainCount[17357:1229839] 3------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.729 retainCount[17357:1229839] 1------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.730 retainCount[17357:1229839] 2------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:42.731 retainCount[17357:1229839] 3------所在线程<NSThread: 0x600000263980>{number = 1, name = main}
2017-05-05 15:34:43.561 retainCount[17357:1232087] 1------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:43.561 retainCount[17357:1232087] 2------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:43.562 retainCount[17357:1232087] 3------所在线程<NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232087] 任务1  == <NSThread: 0x60800026b4c0>{number = 3, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232083] 任务3  == <NSThread: 0x600000276e80>{number = 5, name = (null)}
2017-05-05 15:34:44.402 retainCount[17357:1232112] 任务2  == <NSThread: 0x60800026bf40>{number = 4, name = (null)}

具体的分析统一写在这里吧

首先还是得记住前面所写的

  • 同步异步决定是否开启线程
  • 并行串行决定是否顺序执行任务
  • 主队列中有主线程,在其中的任务不会创建新的线程
    记住这三条就可以了
    好了,开始分析
  1. 子线程、同步任务、主队列
    看这个解释之前先看一下第二中情况,这一次我们在子线程中开启了同步任务,然后block放在主队列,我们的同步任务只要等到block执行就可以了,不会死锁。

  2. 主线程、同步任务、主队列
    关于主线程和主队列的概念,我看了好几个博客,还是不太懂,不过有一个原则就是主队列中的任务要在主线程中执行,有了这个原则可能就好解释了。我们在主线程中调用了同步函数,调用的同步函数的操作就在block中。但是block被我们放在了主队列中,主队列是串行队列,block在这个队列的最后。前面的所有任务如果无法完成就不会调用到block。但是前面任务完成的条件是同步函数要完成。于是就死锁了。

  3. 主线程、异步任务、主队列
    唯一和以上不同的是,异步任务的优先级低,不会抢夺系统资源,会在cpu空闲的时候调用,因此不会造成死锁。

  4. 同步任务、并发队列
    同步任务不会创建新的线程,因此会在主线程中调用

  5. 同步任务、串行队列
    同上

  6. 异步任务、串行队列
    异步任务会开启新的线程,因为串行队列,只开启一个新的线程

  7. 异步任务、并行队列
    异步任务会开启新的线程,因为并行队列,会开启多个线程

五、线程通信

举个图片下载的🌰

- (void)threadCommunication
{
    //创建一个队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    //异步任务
    dispatch_async(queue, ^{
        //下载图片
        NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493985090619&di=43c16e2e96b2bd4f2cb694ffd7ad0c0c&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fforum%2Fw%3D580%2Fsign%3Daa1a5e38afec8a13141a57e8c7029157%2F0092bc389b504fc22854cad2e3dde71191ef6d1e.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        //主线程更新ui
        dispatch_sync(dispatch_get_main_queue(), ^{
            self.ThreadImageView.image = image;
        });
        NSLog(@"图片下载结束");
    });
}

运行结果

线程通信.png

五、队列组

使用场景:等俩那个异步操作执行结束后执行某项操作,栅栏函数也可以实现
关键函数

dispatch_group_t//创建队列组
dispatch_group_async//队列其中一个异步任务
dispatch_ground_notify//所有异步操作结束后的操作

示例代码(附带图片绘制和缓存沙盒)

    dispatch_queue_t queue = dispatch_queue_create("yfq", DISPATCH_QUEUE_CONCURRENT);
    __block UIImage *image1 = nil;
    __block UIImage *image2 = nil;
//block内部可以使用block外部的变量,如果需要改变block外部变量,需要添加__block修饰符
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494220438978&di=5fb8127873860c26a59e9ef775030528&imgtype=0&src=http%3A%2F%2Fd.5857.com%2Fjryh_170321%2Fdesk_001.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        image1 = [UIImage imageWithData:data];
        NSLog(@"图片1下载完成");
    });
    dispatch_group_async(group, queue, ^{
        NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1494220164621&di=8bd30fe6a3bde6463dc5dd5c0684009a&imgtype=0&src=http%3A%2F%2Fimage.tianjimedia.com%2FuploadImages%2F2015%2F164%2F22%2FX3UBUK29Z641.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        image2 = [UIImage imageWithData:data];
        NSLog(@"图片2下载完成");
    });
    
    dispatch_group_notify(group, queue, ^{
        UIGraphicsBeginImageContext(CGSizeMake(200, 100));
        [image1 drawInRect:CGRectMake(0, 0, 100, 100)];
        [image2 drawInRect:CGRectMake(100, 0, 100, 100)];
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        dispatch_async(dispatch_get_main_queue(), ^{
            self.GrounpImageView.image = newImage;
            NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent:@"Documents"] stringByAppendingPathComponent:@"123.png"];
            [UIImagePNGRepresentation(newImage) writeToFile:path atomically:YES];
        });
    });

沙盒中的截图

sandboxImage.png

通过NSHomeDirectory()打印直接进入模拟器的沙盒,可以看到图片保存了。
图片绘制照搬大体分为几个步骤吧

  1. UIGraphicsBeginImageContext(cg)//绘制区域
  2. [image drawInRect:cg];//添加内容
  3. UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();//获取绘制内容的图片
  4. UIGraphicsEndImageContext();//关闭图形上下文

绘制图片这块好像有点复杂,一句两句说不清,有空再开一篇说吧。
Markdown自带的表格代码不好用(可能是我没用对),使用没效果,不过自动保存很好用,再也不用担心不小心关闭浏览器了。
【转载并整理于http://www.jianshu.com/p/bc45569adee2

相关文章

  • 整理自一篇gcd简书

    一、一个异步、同步、并行、串行的表格 (第一个使用Markdown)多核是gcd的基础 二、任务:同步、异步、栅栏...

  • 【Objective-C】GCD介绍

    整理自raywenderlich。 1.GCD是嘛? GCD是Grand Central Dispatch的缩写,...

  • (转)gcd简单使用和介绍

    本文转载自:时间已静止的简书:iOS多线程实现——GCD使用详解 一、介绍 GCD,英文全称是Grand Cent...

  • 多线程之GCD学习demo

    之前看到简书有同行总结出GCD的初级用法文章,我就整理出来这篇文章的demo,希望对大家有帮助 GCD #####...

  • (转)iOS多线程:『GCD』详尽总结

    本文转载自:行走的少年郎的简书:iOS多线程:『GCD』详尽总结 本文用来介绍 iOS 多线程中 GCD 的相关知...

  • GCD整理(一)

    整理一篇关于GCD的文章,自己以后要复习的时候也方便。 GCD(Grand Center Dispatch)异步执...

  • iOS多线程:【GCD】】详尽总结

    此篇文章只是自学照别人抄的 iOS多线程:『GCD』详尽总结 - 简书 1.GCD简介 什么是GCD GCD是 A...

  • iOS开发GCD

    有关任务和队列的概念在此就不多说了,我的简书有详细记载,上一篇是基础,这一篇是针对-----GCD。 一、初识GC...

  • 第一篇 简书

    第一篇 简书第一篇 简书第一篇 简书第一篇 简书第一篇 简书第一篇 简书第一篇 简书

  • 【公告】关于简书大学堂讲师解约的说明171024

    自10月起,简书大学堂做了简书签约作者的整理和调整,简书大学堂讲师是简书签约作者品牌的下设子品牌之一,简书作者与简...

网友评论

      本文标题:整理自一篇gcd简书

      本文链接:https://www.haomeiwen.com/subject/gkmjtxtx.html