zoukankan      html  css  js  c++  java
  • 通过Dispatch Group机制,根据系统资源状况来执行任务

    dispatch group是GCD的一项特性,能够把任务分组,调用者可以等待这组任务执行完毕,也可以在提供回调函数之后继续往下执行,这组任务完成时,调用者会得到通知。

    这个功能有多个用途,其中最重要,最值得注意的用法,就是把将要并发执行的多个任务合为一个组,于是调用者就可以知道这些任务合适才能全部执行完毕。

    举个栗子:把一系列压缩文件的任务表示成dispatch group。

    下面这个函数可以创建dispatch group:

      dispatch_group_tdispatch_group_create();

    dispatch group就是哥简单的数据结构,这种结构彼此之间没什么区别,它不像派发队列,后者还有个用来区别身份的标示符,想把任务编组,有两种办法,第一种是用下面这个函数:

    void dispatch_group_async(dispatch_group_t group,

                  dispatch_queue_t queue,

                  dispatch_block_t block);

    它是普通dispatch_async函数的变体,比原来多了一个参数,用于表示待执行的块所归属的组。还有中办法能够制定任务所属的dispatch group,那就使用下面这对函数:

    void dispatch_group_enter(dispatch_group_t group)

    void dispatch_group_leave(dispatch_group_t group)

    前者能够使分组里正要执行的任务数递增,而后者则使之递减。由此可知,调用了dispatch_group_enter以后,必须有与之对应的dispatch_group_leave才行。这与引用计数相似,要使用引用计数,就必须令保留操作与释放操作彼此对应以防内存泄露。而在使用dispatch group时,如果调用enter之后,没有相应的leave操作,那么这一组任务就永远执行不完。

    下面这个函数可以用于等待dispatch group执行完毕:

      long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

    此函数接受两个参数,一个是要等待的group,另一个是代表等待的时间timeout值。该参数表示函数在等待dispatch group执行完毕是,应该阻塞多久。如果执行dispatch group所需的时间小于timeout,则返回0,否则返回非零。这个参数也可以取常量DISPATCH_TIME_FOREVER,这表示函数会一直等着dispatch group执行完,而不会超时(time out)。

    除了可以用上面那个函数等待dispatch group执行完毕之外,也可以换个办法,使用下列函数:

    void dispatch_group_notify(dispatch_group_t group,

                  dispatch_queue_t queue,

                  dispatch_block_t block);

    与wait函数略有不同的是:开发者可以想此函数传入块,等dispatch group执行完毕之后,块会在特定的线程上执行。假如当前线程不应阻塞,而开发者又响在那些人物全部完成时得到通知,那么此做法就很有必要了。

    如果想令数组中的每个对象都执行某项任务,并且想等待所有任务执行完毕,那么就可以使用这个GCD特性来实现。代码如下:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);
    dispatch_group_t dispatchGroup = dispatch_group_create();
    for (id object in collection){
        dispatch_group_async(dispatchGroup,queue,^{
            [object performTask];
        );      
    }                            
    diapatch_group_wait(dispatchGroup,DISPATCH_TIME_FOREVER);

    若当前线程不应阻塞,则可用notify函数来取代wait:

    dispatch_queue_t notifyQueue = dispatch_get_main_queue();
    
    dispatch_group_notify(dispatchGroup,notifyQueue,^{
      //continue processing after completing tasks
    })

    notify回调时所选用的队列,完全应该根据具体情况来定,逼着在范例代码中使用了主队列,这是种常见写法。也可以用自定义的串行队列或全局并发队列。

    在本例中,所有任务都派发到同一个队列之中。但实际上未必一定要这样做。也可以把某些任务放在优先级高的线程上执行,同时仍然把所有任务都归入同意个dispatch group,并在执行完毕时获得通知:

     dispatch_queue_t lowPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0);
    
    dispatch_queue_t highPriorityQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
    dispatch_group_t dispatchGroup = dispatch_group_create();
    
    for(id object in lowPriorityObjects){
    
      dispatch_group_async(dispatchGroup,lowPriorityQueue,^{
    
        [object performTask];
    
      }
    
    } 
    
    for(id object in highPriorityObjects){
    
      dispatch_group_async(dispatchGroup,highPriorityQueue,^{
    
        [object performTask];
    
      }
    
    } 
    
    dispatch_queue_t notifyQueue = dispatch_get_main_queue();
    
    dispatch_group_notify(dispatchGroup,notifyQueue,^{
    
      //continue processing after completing tasks  
    
    });

    除了像上面这样把任务提交到并发队列之外,也可以把任务提交至各个串行队列中,并用dispatch group跟踪其执行状况,然而,如果所有任务都排在同一个串行队列里面,那么dispatchgroup用处就不大了。因为此时任务总要逐个执行,所以只需在提交完全部任务之后再提交一个块即可,这样做与通用notify函数等待dispatch group执行完毕然后再回调块是等效的:

    dispatch_queue_t queue = dispatch_queue_create("com.effectiveobjectivc.queue",NULL);
    
    for(id object in collection){
    
      dispatch_async(queue,^{
    
        [object perfomTask];
      });
    
    }
    
    dispatch_async(queue,^{
    
    //continue processing after completing tasks
      }
    
    );

    上面这段代码表明,开发者未必总需要使用dispatch group,有时候采用单个队列搭配标准的异步派发,也可实现同样效果。

    笔者为何要在标题中谈到“根据系统资源状况来执行任务”呢?回头看看向并发队列派发任务的那个例子,就明白了。为了执行队列中的块,GCD会在适当的时机自动创建新线程或复用旧线程。如果使用并发队列,那么其中有可能会有多个线程,这也就意味着多个块可以并发执行。在并发队列中,执行任务所用的并发线程数量,取决于各种因素,而GCD主要是根据系统资源状况来判定这些因素的,加入CPU有多个核心,并且队列中又大量任务等待执行,那么GCD就可以能会给该队列配备多个线程,通过dispatch group所提供的这种简便方式,既可以并发执行一系列给定的任务,又能在全部任务结束时得到通知。由于GCD有并发队列机制,所以能够根据可用的系统资源状况来并发执行任务。而开发者则可以专注于业务逻辑代码,无须再为了处理并发任务而编写复杂的调度器。

    在前面的范例代码中,我们便利某个collection,并在其每个元素上执行任务,而这也可以用另外一个GCD函数来实现:

    void dispatch-apply(size_t iterations,dispatch_queue_t queue, void(^block)(size_t));

    此函数会将块反复执行一定的次数,每次传给块的参数值都会递增,从0开始,直至"iterations-1".其用法如下:

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

    dispatch_apply(10,queue,^(size_t i){

        //perform task

      }

    )

    采用简单的for 循环,从0递增至9,也能实现同样的效果:

    for(int i = 0;i<10;i++){//perform task}

    有一件事需要注意:dispatch_apply所用的队列可以是并发队列,如果采用并发队列,那么系统可以根据资源状况来并行执行这些块了,这与使用dispatch group的那段范例代码一样。上面这个for循环要处理的collection若是数组,则可用dispatch_apply改写如下:

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

    dispatch_queue_apply(array.coun,queue,^(size_t i){

        id object = array[i];

        [object performTask];  

      };

    );

    这个例子再次表明:未必总要使用dispatch_group,然而,dispatch_apply会持续阻塞,直到所有任务都执行完毕为止。由此可以见加入把块派给当前队列(或者体系中高于当前队列的某个串行队列),就讲导致死锁,若想在后台执行任务,则应使用dispatch group。

    要点:

    • 一系列任务可归入一个dispatch group中,开发者可以在这组任务执行完毕时获得通知
    • 通过dispatch group,可以在并发式派发队列里同时执行多项任务。此时GCD会根据系统资源状况来调度这些并发执行的任务。开发者若是自己来实现此功能,则需编写大量代码

    摘自 《编写高质量iOS与OS X代码的52个有效方法》 第四十四条。

  • 相关阅读:
    JSON——JavaScript 对象表示法(JavaScript Object Notation)。
    jquery—— jQuery 是目前最流行的 JS 框架
    Bootstrap的css
    markdown编辑器与富文本编辑器优缺点比较--转载好文
    MATLAB绘图设置--中坐标显示间隔以及范围设置
    MATLAB绘图中参数导数的标注方法
    MATLAB 读取和写出Excel文件的方法-单个以及自动循环读写多个情况
    matlab 求和
    MATLAB画图中设置Marker过于密集怎么办-解决方法
    MPC特点原理以及各项参数的选择--引用别人的博客网址
  • 原文地址:https://www.cnblogs.com/lingzhiguiji/p/4009346.html
Copyright © 2011-2022 走看看