GCD

作者: i赵磊 | 来源:发表于2016-06-08 16:50 被阅读963次
    走进GCD
    前言:本文章第一时间出现在一个程序猿的秘密基地专题中,如果您喜欢类似的文章,请您点击一个程序猿的秘密基地进行关注,在以后的日子里与小编共同成长!

    1.GCD是什么?
    2.GCD的优势
    3.GCD的核心理念
    4.GCD的队列类型
    5.自定义串行和并行队列
    6.全局并行队列的优先级
    7.主线程中的串行队列
    8.调度组
    9.调度组(扩展)
    10.造成线程死锁的两种情况
    11.线程锁(NSLock)
    12.通过GCD实现单例类(ARC)
    13.通过GCD实现单例类(MRC)


    1.GCD是什么?

       GCD全称Grand Central Dispath(宏大的的中央调度),是苹果开发的一种支持并行操作的机制。
       它的主要部件是一个FIFO队列和一个线程池,前者用来添加任务,后者用来执行任务。
       GCD中的FIFO队列称为dispatch queue,它可以保证先进来的任务先得到执行(但不
    保证一定先执行结束)。
       GCD中的函数大多数都以dispatch开头
    

    2.GCD的优势

    1.它是苹果公司为多核的并行运算提出的解决方案
    2.它在工作的时候会自动利用更多的处理器核心
    3.不用关心线程代码的实现,即不需要关心什么时候开启线程、关闭线程,GCD会负责创建线程调度你的任务
    4.只需要创建出来你要执行的任务,然后把任务添加到适当的队列中
    

    3.GCD的核心理念

    将长期运行的任务拆分成多个工作单元,并把这些工作单元添加调度队列
    

    4.GCD的队列类型

    分为两种类型:
    1.串行队列(需要自己创建队列):排成一队,一次只能执行一个任务,当前的任务完成之后开始出列,
      启动下一个任务
    2.并行队列(不需要自己创建队列):同时排成N个队,可以让多个任务同时执行,即启动多个线程同时
      执行任务,并发功能只有在异步(dispatch_async)函数下才有效
    ps:并行队列依旧按照任务添加的顺序启动任务,但是,后一个任务无须等待前一个任务执行完毕,而是
      启动第一个任务后,立即启动下一个任务。至于同一时刻允许同时运行多少个任务由系统决定,任务各自
      运行在并行队列为它们提供的独立线程上,并行队列中同时运行多少个任务,就必须维护多少个线程。
    

    5.自定义串行和并行队列。

    /*
     *  (1)串行队列,添加同步任务
     *  串行队列中的任务按照添加顺序执行,不会开启新线程.
     */
    - (void)demo1
    {
        //创建串行队列
        //参数1:表示队列的标识符,一般以反域名的形式命名
        //参数2:队列样式,DISPATCH_QUEUE_SERIAL或者NULL,表示串行队列
        dispatch_queue_t mySerialQueue = dispatch_queue_create("com.zhiyou.aaa", DISPATCH_QUEUE_SERIAL);
        
        //将任务添加到队列中.
        //sync(同步):阻塞当前线程,当block块中的任务执行完毕之后,再执行后续代码
        NSLog(@"111111");
        
        dispatch_sync(mySerialQueue, ^{
            NSLog(@"--------%@",[NSThread currentThread]);
            for (int i = 0; i < 100; i++)
            {
                NSLog(@"2222222");
            }
            
        });
        dispatch_sync(mySerialQueue, ^{
            NSLog(@"--------%@",[NSThread currentThread]);
            for (int i = 0; i < 100; i++)
            {
                NSLog(@"33333333");
            }
            
        });
        
        for (int i = 0; i < 100; i++)
        {
            NSLog(@"44444444");
        }
        
    }
    
    /*
     * (2)串行队列,添加异步任务.
     * 只开辟一条新线程,新线程中的任务还是按照添加顺序执行,但是与主线程中任务是并发执行.
     */
    - (void)demo2
    {
        dispatch_queue_t mySerialQueue = dispatch_queue_create("com.zhiyou.bbb", DISPATCH_QUEUE_SERIAL);
        //async(异步):不阻塞当前线程.block块中的任务与后续代码同时执行.
        NSLog(@"1111111");
        
        dispatch_async(mySerialQueue, ^{
            NSLog(@"------%@",[NSThread currentThread]);
            
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"22222");
            }
        });
        dispatch_async(mySerialQueue, ^{
            NSLog(@"------%@",[NSThread currentThread]);
            
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"33333");
            }
        });
        
        for (int i = 0; i < 50; i++)
        {
            NSLog(@"44444");
        }
    }
    
    /*
     * (3)并行队列,添加同步任务(并无任何意义)
     * 不会开辟新线程,任务按添加顺序执行.
     */
    - (void)demo3
    {
        //DISPATCH_QUEUE_CONCURRENT:表示并行队列
        dispatch_queue_t myConcurrentQurue = dispatch_queue_create("com.zhiyou.ccc", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1111");
        
        dispatch_sync(myConcurrentQurue, ^{
            NSLog(@"-----%@",[NSThread currentThread]);
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"2222");
            }
        });
        dispatch_sync(myConcurrentQurue, ^{
            NSLog(@"-----%@",[NSThread currentThread]);
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"333");
            }
        });
        dispatch_sync(myConcurrentQurue, ^{
            NSLog(@"-----%@",[NSThread currentThread]);
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"444");
            }
        });
        
        for (int i = 0; i < 50; i++)
        {
            NSLog(@"5555");
        }
    }
    /*
     * 并行队列,添加异步任务
     * 会开辟多条新线程,任务并发执行.
     */
    - (void)demo4
    {
        dispatch_queue_t myConcurrentQurue = dispatch_queue_create("com.zhiyou.ccc", DISPATCH_QUEUE_CONCURRENT);
        NSLog(@"1111");
        
        dispatch_async(myConcurrentQurue, ^{
            NSLog(@"-----%@",[NSThread currentThread]);
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"2222");
            }
        });
        dispatch_async(myConcurrentQurue, ^{
            NSLog(@"-----%@",[NSThread currentThread]);
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"333");
            }
        });
        dispatch_async(myConcurrentQurue, ^{
            NSLog(@"-----%@",[NSThread currentThread]);
            for (int i = 0; i < 50; i++)
            {
                NSLog(@"444");
            }
        });
        
        for (int i = 0; i < 50; i++)
        {
            NSLog(@"5555");
        }
    
    }
    

    6.全局并行队列的优先级

    /*
     * 2.运行在分线程的四个优先级不同全局并行队列,globalQueue
     * 开辟多条线程,并发执行任务
     */
    - (void)demo5
    {
        //获取 globalQueue
        //参数1:表示队列的优先级
        //参数2:预留参数,设置为0.
        dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
       
        
        dispatch_queue_t globalQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        dispatch_queue_t globalQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
        
        dispatch_async(globalQueue, ^{
            NSLog(@"------%@",[NSThread currentThread]);
            for (int i = 0; i < 5; i++)
            {
                NSLog(@"2222");
            }
        });
        
        
       
        
        dispatch_async(globalQueue1, ^{
            NSLog(@"------%@",[NSThread currentThread]);
            for (int i = 0; i < 5; i++)
            {
                NSLog(@"1111111");
            }
        });
    
        
        
        
       
        dispatch_async(globalQueue2, ^{
            NSLog(@"------%@",[NSThread currentThread]);
            for (int i = 0; i < 5; i++)
            {
                NSLog(@"000000");
            }
        });
    
        
        
            for (int i = 0; i < 5; i++)
            {
                NSLog(@"5555");
            }
    }
    
    

    7.主线程中的串行队列

    PS:在主线程中,主队列中使用同步任务会造成死锁
    /*
     主队列
     运行在主线程的串行队列,mainQueue。
     */
    - (void)demo6
    {
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        NSLog(@"11111");
        dispatch_async(mainQueue, ^{
           
            NSLog(@"-----%@",[NSThread currentThread]);
            for (int i = 0; i < 20; i++)
            {
                NSLog(@"22222");
            }
            
        });
        for (int i = 0; i < 20; i++)
        {
            NSLog(@"33333");
        }
    }
    
    

    8.调度组

    //  群组-统一监控一组任务
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_queue_t q = dispatch_get_global_queue(0, 0);
        // 添加任务
        // group 负责监控任务,queue 负责调度任务
        dispatch_group_async(group, q, ^{
            [NSThread sleepForTimeInterval:1.0];
            NSLog(@"任务1 %@", [NSThread currentThread]);
        });
        dispatch_group_async(group, q, ^{
            NSLog(@"任务2 %@", [NSThread currentThread]);
        });
        dispatch_group_async(group, q, ^{
            NSLog(@"任务3 %@", [NSThread currentThread]);
        });
       
        // 监听所有任务完成 - 等到 group 中的所有任务执行完毕后,"由队列调度 block 中的任务异步执行!"
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            // 修改为主队列,后台批量下载,结束后,主线程统一更新UI
            NSLog(@"OK %@", [NSThread currentThread]);
        });
        
        NSLog(@"come here");
    

    9.调度组(扩展)

    调度组中还有一个添加任务的函数:
    dispatch_group_enter(dispatch_group_t group)
    dispatch_group_leave(dispatch_group_t group)
    这两个函数要配对出现 例如:

    // 群组-统一监控一组任务
    dispatch_group_t group = dispatch_group_create();
     
    dispatch_queue_t q = dispatch_get_global_queue(0, 0);
     
    // 1> 入组 -> 之后的 block 会被 group 监听
    // dispatch_group_enter 一定和 dispatch_group_leave 要配对出现
    dispatch_group_enter(group);
    dispatch_async(q, ^{
        NSLog(@"task1 %@", [NSThread currentThread]);
         
        // block 的末尾,所有任务执行完毕后,添加一个出组
        dispatch_group_leave(group);
    });
     
    //  再次入组
    dispatch_group_enter(group);
    dispatch_async(q, ^{
        [NSThread sleepForTimeInterval:1.0];
         
        NSLog(@"task2 %@", [NSThread currentThread]);
         
        // block 的末尾,所有任务执行完毕后,添加一个出组
        dispatch_group_leave(group);
    });
     
    // 群组结束
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"OVER");
    });  
    NSLog(@"come here");
    

    10.造成线程死锁的两种情况

    《1》请回看本文中第七条 条目下的注释,讲述的是第一种死锁情况。
    《2》串行队列在使用的过程中容易出现死锁,调用 同步函数的队列 和 同步函数添加任务的目标队列 是同一个队列.
    如果不是同一个队列, 是没有问题的.
    dispatch_queue_t queue = dispatch_queue_create("com.zhiyou.queue", DISPATCH_QUEUE_SERIAL);
     NSLog(@"任务一");
    dispatch_sync(queue, ^{
    NSLog(@"任务二");
    //queue队列在调用同步函数
            dispatch_sync(queue, ^{
               
                NSLog(@"任务三");
            });
            
            NSLog(@"任务四");
        });
        
        NSLog(@"任务五");
    

    11.线程锁(NSLock)

    //Demo介绍:模拟售票员买票,展示不加锁和加锁的区别
    #import "ViewController.h"
    
    @interface ViewController ()
    {
        NSInteger _ticketNum;
        NSLock *_lock;
    }
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        [self sellTicket];
    }
    
    - (void)sellTicket
    {
        //所剩下的票数
        _ticketNum = 6;
        
        //创建锁
        _lock = [[NSLock alloc]init];
    
        NSThread *theard1 = [[NSThread alloc]initWithTarget:self selector:@selector(buyTicket) object:nil];
        theard1.name = @"售票员1";
        [theard1 start];
        
        NSThread *theard2 = [[NSThread alloc]initWithTarget:self selector:@selector(buyTicket) object:nil];
        theard2.name = @"售票员2";
        [theard2 start];
        
        NSThread *theard3 = [[NSThread alloc]initWithTarget:self selector:@selector(buyTicket) object:nil];
        theard3.name = @"售票员3";
        [theard3 start];
        
    }
    - (void)buyTicket
    {
        while (YES)
        {
            //锁
    //        [_lock lock];
            
     
            
                if (_ticketNum > 0)
                {
                    [NSThread sleepForTimeInterval:0.1];
                    
                    _ticketNum--;
                    
                    NSLog(@"%@卖了一张票,还剩余 %d 张票",[NSThread currentThread].name,_ticketNum);
                }
                else
                {
                    return;
                }
            
            //解锁
    //        [_lock unlock];
        }
    }
    @end
    
    下面是我捕捉到的不加锁控制面板的输入️

    ·


    情况一:错序
    情景二:出现两个售票员同时操作同一张票,导致票数错误

    12.通过GCD实现单例类(ARC)

    #import "Singleton.h"
    
    static Singleton *singleton = nil;
    
    @implementation Singleton
    
    //通过GCD来实现单例模式
    + (instancetype)shareSingleton
    {
        //参数1:谓词,用来判断block块是否已经被执行.
        //参数2:只会被调用一次的block
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            
            singleton = [[super allocWithZone:NULL]init];
            
        });
        
        return singleton;
    }
    + (id)allocWithZone:(struct _NSZone *)zone
    {
        return [self shareSingleton];
    }
    
    
    @end
    
    

    13.通过GCD实现单例类(MRC)

    danlilei.h

    #import <Foundation/Foundation.h>
    
    @interface danlilei : NSObject
    +(instancetype)danlilei;
    
    @end
    
    

    danlilei.m

    //需要确保单例类的原则:一个类只有一个对象。所以要重写以下所有方法
    #import "danlilei.h"
    static danlilei4*danli=nil;
    @implementation danlilei
    +(instancetype)danlilei
    {
        @synchronized(self)
        {
            danli=[danlilei4 new];
        }
        return danli;
    }
    -(id)init
    {
        self=[super init];
        if (self)
        {
            
        }
        return self;
    }
    +(instancetype)allocWithZone:(struct _NSZone *)zone
    {
        return [self danlilei];
    }
    +(id)copyWithZone:(struct _NSZone *)zone
    {
        return [self danlilei];
    }
    -(instancetype)retain
    {
        return danli;
    }
    -(oneway void)release
    {
        
    }
    -(id)autorelease
    {
        return danli;
    }
    -(NSUInteger)retainCount
    {
        return 1;
    }
    
    @end
    

    相关文章

      网友评论

      本文标题:GCD

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