美文网首页
整理自一篇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简书

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