52个有效方法(44)|52个有效方法(44) - 通过Dispatch Group机制,根据系统资源状况来执行任务

dispatch group是GCD的一项特性,能够把任务分组。调用者可以等待这组任务执行完毕,也可以在提供回调函数之后继续往下执行,这组任务完成时,调用者会得到通知。这个功能有许多用途,其中最重要、最值得注意的用法,就是把将要并发执行的多个任务合为一组,于是调用者就可以知道这些任务何时才能全部执行完毕。

  • dispatch_group_create 创建dispatch group
    dispatch_group_t group = dispatch_group_create();

  • dispatch_group_async把异步任务提交到指定任务组和指定下拿出队列执行。
    void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);

    • group ——对应的任务组,之后可以通过dispatch_group_wait或者dispatch_group_notify监听任务组内任务的执行情况。
    • queue ——block任务执行的线程队列,任务组内不同任务的队列可以不同。
    • block —— 执行任务的block。
  • dispatch_group_enter用于添加对应任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数加1,当未执行完毕任务数为0的时候,才会使dispatch_group_wait解除阻塞和dispatch_group_notify的block执行。
    void dispatch_group_enter(dispatch_group_t group);

  • dispatch_group_leave用于减少任务组中的未执行完毕的任务数,执行一次,未执行完毕的任务数减1,dispatch_group_enterdispatch_group_leave要匹配,不然系统会认为group任务没有执行完毕。
    void dispatch_group_leave(dispatch_group_t group);

  • dispatch_group_wait等待组任务完成,会阻塞当前线程,当任务组执行完毕时,才会解除阻塞当前线程。
    long dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout);

    • group ——需要等待的任务组。
    • timeout ——等待的超时时间(即等多久),单位为dispatch_time_t。如果设置为DISPATCH_TIME_FOREVER,则会一直等待(阻塞当前线程),直到任务组执行完毕。
  • dispatch_group_notify待任务组执行完毕时调用,不会阻塞当前线程。
    void dispatch_group_notify(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);

    • group ——需要监听的任务组。
    • queue ——block任务执行的线程队列,和之前group执行的线程队列无关。
    • block ——任务组执行完毕时需要执行的任务block。
dispatch_semaphore dispatch_semaphore_create
dispatch_semaphore_tdispatch_semaphore_create(long value);

  • 传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。
  • 值得注意的是,这里的传入的参数value必须大于或等于0,否则 dispatch_semaphore_create会返回NULL。
dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

  • 这个函数会使传入的信号量dsema的值加1。
  • 【52个有效方法(44)|52个有效方法(44) - 通过Dispatch Group机制,根据系统资源状况来执行任务】dispatch_semaphore_signal的返回值为long类型,当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。
  • 当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。
dispatch_semaphore_wait
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

  • 这个函数会使传入的信号量dsema的值减1。
  • 这个函数的作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1。
  • 如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,不能直接传入整形或float型数)。
  • 如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。
  • 如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。
  • dispatch_semaphore_wait的返回值也为long型。当其返回0时表示在timeout之前,该函数所处的线程被成功唤醒。当其返回不为0时,表示timeout发生。
DISPATCH_TIME_NOWDISPATCH_TIME_FOREVER
  • 在设置timeout时,比较有用的两个宏:DISPATCH_TIME_NOWDISPATCH_TIME_FOREVER
- DISPATCH_TIME_NOW表示当前;- DISPATCH_TIME_FOREVER表示遥远的未来;

  • 一般可以直接设置timeout为这两个宏其中的一个,或者自己创建一个dispatch_time_t类型的变量。
  • 创建dispatch_time_t类型的变量有两种方法,dispatch_timedispatch_walltime
  • 利用创建dispatch_time创建dispatch_time_t类型变量的时候一般也会用到这两个变量。
dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);

  • 其参数when需传入一个dispatch_time_t类型的变量,和一个delta值。表示when加delta时间就是timeout的时间。
dispatch_time_tt = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000); //表示当前时间向后延时一秒为timeout的时间。

实例代码
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSLog(@"group one start"); dispatch_group_async(group, queue, ^{ dispatch_async(queue, ^{ sleep(1); //这里线程睡眠1秒钟,模拟异步请求 NSLog(@"group one finish"); }); }); dispatch_group_notify(group, queue, ^{ NSLog(@"group finished"); });

  • 在group中嵌套了一个异步任务时,group并没有等待group内的异步任务执行完毕才进入dispatch_group_notify中,这是因为,在dispatch_group_async中又启了一个异步线程,而异步线程是直接返回的,所以group就认为是执行完毕了。
  • 通过dispatch_group_enter告知group,一个任务开始,未执行完毕任务数加1,在异步线程任务执行完毕时,通过dispatch_group_leave告知group,一个任务结束,未执行完毕任务数减1,当未执行完毕任务数为0的时候,这时group才认为组内任务都执行完毕了(这个和GCD的信号量的机制有些相似),这时候才会回调dispatch_group_notify中的block。
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSLog(@"group one start"); dispatch_group_enter(group); dispatch_async(queue, ^{ sleep(1); //这里线程睡眠1秒钟,模拟异步请求 NSLog(@"group one finish"); dispatch_group_leave(group); }); dispatch_group_notify(group, queue, ^{ NSLog(@"group finished"); });

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

    推荐阅读