美文网首页网络多线程iOS Developer程序员
iOS--GCD的API的理解与使用

iOS--GCD的API的理解与使用

作者: 黑白灰的绿i | 来源:发表于2017-04-10 16:06 被阅读71次

    什么是GCD?

    Grand Central Dispatch(GCD)是异步执行任务的技术之一。一般将将应用程序中记述的线程管理用的代码在系统级中实现。开发者只需定义想执行的任务并追加到适当的Dispatch Queue中,GCD就能生成必要的线程并计划执行任务,由于线程管理是作为系统的一部分来实现的,因此可统一管理,也可执行任务,这样就比以前的线程更有效率。

    Dispatch Queue

    含义是,执行处理的等待队列。
    在执行处理时有两种Dispatch Queue

    Serial Dispatch Queue          //  等待现在执行中处理结束
    
    Concurrent Dispatch Queue      //  不等待现在执行中处理结束
    

    举例对比:

    dispatch_async(queue, blk1);
    dispatch_async(queue, blk2);
    dispatch_async(queue, blk3);
    dispatch_async(queue, blk4);
    dispatch_async(queue, blk5);
    dispatch_async(queue, blk6);
    dispatch_async(queue, blk7);
    

    当queue为Serial Dispatch Queue 时,因为需要等待现在执行中的处理结果,所以程序会相继执行1234567。
    当queue为Concurrent Dispatch Queue时,因为不需要等待现在执行中的处理结果,所以先执行1,无论1有没有执行完都会执行2,以此类推,顺序是会改变的。此中执行方法开启的线程数量取决于当前系统的状态。

    dispatch_queue_create

    通过GCD的API生成Dispatch Queue。

    dispatch_queue_t queue=dispatch_queue_create("com.like.same", NULL);
    

    第一个参数指定Serial Dispatch Queue的名称,该名称会出现在应用程序崩溃时所生成的CrashLog中。可以但是不推荐为NULL。
    第二个参数指定为NULL。
    dispatch_queue_create函数可生成人一多个Dispatch Queue。虽然在一个Serial Dispatch Queue中同时只能执行一个追加处理,但是如果将处理分别追加到四个Serial Dispatch Queue中,各个Serial Dispatch Queue执行一个,即为同时执行四个处理。
    通过dispatch_queue_creat函数生成的Dispatch Queue在使用结束后通过dispatch release函数释放。

    dispatch_release(queue);
    
    多个线程更新相同资源导致数据竞争时使用Serial Dispatch Queue。

    Main Dispatch Queue/Global Dispatch Queue

    获取系统标准提供的Dispatch Queue。

        //   Main Dispatch Queue的获取方法
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        //  Global Dispatch Queue高优先级获取方法
        dispatch_queue_t highQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
        
        //   Global Dispatch Queue默认优先级获取方法
        dispatch_queue_t defaultQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        //   Global Dispatch Queue低优先级获取方法
        dispatch_queue_t lowQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
        
        //   Global Dispatch Queue后台优先级的获取方法
        dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    

    dispatch_set_target_queue

    变更生成的Dispatch Queue的执行优先级要使用dispatch_set_target_queue函数。
    eg:

        dispatch_queue_t myQueue = dispatch_queue_create("com.like.same", NULL);
        
        dispatch_queue_t backgroundQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
        
        dispatch_set_target_queue(myQueue, backgroundQueue);
    

    在必须将不可并行执行的处理追加到多个Serial Dispatch Queue中时,如果使用dispatch_set_target_queue 函数将目标指定为某一个Serial Dispatch Queue,即可防止处理并行执行。

    dispatch_after

    指定时间后执行处理的情况,可使用dispatch_after函数来实现。
    eg:

        dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
        
        dispatch_after(time, dispatch_get_main_queue(), ^{
            
            NSLog(@"666");
            
        });
    

    第一个参数时指定时间用的dispatch_time_t类型的的值。
    第二个参数是指定要追加处理的Dispatch Queue。
    dispatch_time函数能够获取从第一个参数dispatch_time_t类型值中指定的时间开始,到第二个参数指定的单位时间后的时间。上面的代码中第一个参数代表现在。
    dispatch_time函数通常用于计算相对时间,而dispatch_walltime函数用语计算绝对时间。
    eg:

    dispatch_time_t getDispatchTimeByDate(NSDate * date)
    {
        NSTimeInterval interval;
        double second, subsecond;
        struct timespec time;
        dispatch_time_t milestone;
        
        interval = [date timeIntervalSince1970];
        subsecond = modf(interval, &second);
        time.tv_sec = second;
        time.tv_nsec = subsecond * NSEC_PER_SEC;
        milestone = dispatch_time(&time, 0);
        
        return milestone;
    }
    

    Dispatch Group

    再追加到Dispatch Queue中的多个处理全部请求结束后想要执行结果处理,这种情况下使用Dispatch Group。
    eg:

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"blk0");
        });
        dispatch_group_async(group, queue, ^{
            NSLog(@"blk1");
        });
        dispatch_group_async(group, queue, ^{
            NSLog(@"blk2");
        });
        
        dispatch_group_notify(group, queue, ^{
            NSLog(@"done");
        });
    

    打印结果为:

    blk1
    blk0
    blk2
    done
    

    由于多个线程并行执行,所以执行的顺序会发生改变,但是done一定是在最后输出。

    Dispatch Group.png

    另外,在Dispatch Group中也可以使用dispatch_group_wait函数仅等待全部处理执行结束。

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    

    第二个参数为等待的时间这里使用的是永久,也就是当处理未结束,程序将一直等待下去,中途不能取消。
    如果dispatch_group_wait函数的返回值不为0,就意味着虽然经过了指定的时间,但属于Dispatch Group的某一个处理还在执行中,如果返回0,那么全部处理执行结束。
    一旦调用dispatch_group_wait函数,该函数处于调用的状态而不反回。

    dispatch_barrier_async

    dispatch_barrier_async函数会等待追加到Concurrent Dispatch Queue上的并行执行的处理全部结束之后,再将指定的处理追加到该Concurrent Dispatch Queue中。然后由dispatch_barrier_async函数处理完毕之后,Concurrent Dispatch Queue再回复为一般动作(继续并行执行)。
    eg:

        dispatch_async(queue, blk0);
        dispatch_async(queue, blk1);
        dispatch_async(queue, blk2);
        dispatch_barrier_async(queue, blk);
        dispatch_async(queue, blk3);
        dispatch_async(queue, blk4);
        dispatch_async(queue, blk5);
    

    执行结果恒为012在前,blk在中,345在后。

    使用Concurrent Dispatch Queue和dispatch_barrier_async函数可实现高效率的数据库访问和文件访问。

    图例:


    dispatch_barrier_async.png

    dispatch_sync

    dispatch_async中的“async”意味着非同步,就是将指定的Block非同步的追加到指定Dispatch Queue中,dispatch_async函数不做任何等待。
    “sync”则意味着同步,就是将指定的Block非同步的追加到指定Dispatch Queue中,在追加的block结束前,dispatch_sync函数会一直等待。
    eg:

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        dispatch_sync(queue, ^{
            NSLog(@"上面处理已经结束");
        });
    

    一旦调用dispatch_sync函数,那么指定的处理执行结束之前,该函数不会返回。可以说是简易版的dispatch_group_wait。
    dispatch_barrier_async函数的作用是在等待追加的处理全部结束之后,再追加处理到Dispatch Queue中,此外,它还与dispatch_sync函数相同,会等待追加处理的执行结果。
    但是频繁使用dispatch_sync容易造成死锁。
    eg:

     //  造成死锁
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_sync(queue, ^{
            NSLog(@"????");
        });
        
        //   同样造成死锁
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_async(queue, ^{
            dispatch_sync(queue, ^{
                NSLog(@"???");
            });
        });
    

    dispatch_once

    dispatch_once函数是保证在应用程序执行中只执行一次指定处理的API。
    单例模式:

    static Singleton* _instance = nil;
    +(instancetype) shareInstance
    {
        static dispatch_once_t onceToken ;
        dispatch_once(&onceToken, ^{
        _instance = [[super allocWithZone:NULL] init] ;
    }) ;
    return _instance ;
    }
    
    +(id) allocWithZone:(struct _NSZone *)zone
    {
        return [Singleton shareInstance] ;
    }
    
    -(id) copyWithZone:(struct _NSZone *)zone
    {
        return [Singleton shareInstance] ;
    }
    

    dispatch_apply

    该函数是dispatch_sync函数和Dispatch Group的关联API。按照指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束。
    eg:

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        dispatch_apply(10, queue, ^(size_t index) {
            NSLog(@"%zu",index);
        });
    

    由于此函数与dispatch_sync函数一样会等待处理执行结束,所以最好在dispatch_async函数中非同步的执行dispatch_apply函数。

    dispatch_suspend/dispatch_resume

    dispatch_suspend函数挂起指定的Dispatch Queue。

    dispatch_suspend(queue);
    

    dispatch_resume函数恢复指定的dispatch Queue。

    dispatch_resume(queue);
    

    这两个函数对已经执行的处理没有影响。挂起后,追加到Dispatch Queue中但尚未执行的处理在此之后停止执行。而恢复能使这些处理能够继续执行。

    Dispatch Semaphore

    待续。。。。

    相关文章

      网友评论

        本文标题:iOS--GCD的API的理解与使用

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