zoukankan      html  css  js  c++  java
  • 多个网络请求并发执行、顺序执行

    我们在开发中可能会遇到多个网络请求的需求,比如一个界面有两个请求,tableView的数据源及表头,需要等两个请求都完成才刷新UI,或者第二个的网络请求依赖第一个网络请求返回的数据,今天主要讲下两种方式的实现方式,当然也有其他方式,主要记录下我工作中使用的

    并发执行

    多个网络请求同时执行,等所有网络请求完成,再统一做其他操作,你可能会想到dispatch_group_asyncdispatch_group_notify结合使用,如下:

     1 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     2     dispatch_group_t group = dispatch_group_create();
     3     dispatch_group_async(group, queue, ^{
     4         NSLog(@"任务一完成");
     5     });
     6 
     7     dispatch_group_async(group, queue, ^{
     8         NSLog(@"任务二完成");
     9     });
    10 
    11     dispatch_group_async(group, queue, ^{
    12         NSLog(@"任务三完成");
    13     });
    14     //在分组的所有任务完成后触发
    15     dispatch_group_notify(group, queue, ^{
    16         NSLog(@"所有任务完成");
    17     });

    打印结果:

    1 2017-02-17 09:26:18.668 信号量[53171:2916114] 任务二完成
    2 2017-02-17 09:26:18.668 信号量[53171:2916133] 任务三完成
    3 2017-02-17 09:26:18.668 信号量[53171:2916117] 任务一完成
    4 2017-02-17 09:26:18.669 信号量[53171:2916117] 所有任务完成

    由于三个任务都是异步的,所有返回结果顺序不一定是正序,看似却确实实现了我们的需求,但是网络请求是异步的,耗时的,不是马上就能返回结果的,如下使用dispatch_after模拟网络请求的延迟:

     1 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     2     dispatch_group_t group = dispatch_group_create();
     3     dispatch_group_async(group, queue, ^{
     4         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
     5             NSLog(@"请求一");
     6         });
     7     });
     8 
     9     dispatch_group_async(group, queue, ^{
    10         NSLog(@"任务二完成");
    11     });
    12 
    13     dispatch_group_async(group, queue, ^{
    14         NSLog(@"任务三完成");
    15     });
    16     //在分组的所有任务完成后触发
    17     dispatch_group_notify(group, queue, ^{
    18         NSLog(@"所有任务完成");
    19     });

    打印结果:

    1 2017-02-17 09:30:49.263 信号量[53528:2935734] 任务二完成
    2 2017-02-17 09:30:49.263 信号量[53528:2935737] 任务三完成
    3 2017-02-17 09:30:49.263 信号量[53528:2935737] 所有任务完成
    4 2017-02-17 09:30:50.340 信号量[53528:2935638] 请求一

    可看到任务一的延迟请求完成造成了一些问题,这时候我们可以用信号量即dispatch_semaphore,包括三个部分:dispatch_semaphore_create()dispatch_semaphore_signal()dispatch_semaphore_wait(),三个结合使用,各个参数解释一下:

    • dispatch_semaphore_createdispatch_semaphore_t dispatch_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
    • 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_signal的返回值为long类型,当返回值为0时表示当前并没有线程等待其处理的信号量,其处理的信号量的值加1即可。当返回值不为0时,表示其当前有(一个或多个)线程等待其处理的信号量,并且该函数唤醒了一个等待的线程(当线程有优先级时,唤醒优先级最高的线程;否则随机唤醒)。dispatch_semaphore_wait的返回值也为long型。当其返回0时表示在timeout之前,该函数所处的线程被成功唤醒。当其返回不为0时,表示timeout发生。
    • 在设置timeout时,比较有用的两个宏:DISPATCH_TIME_NOWDISPATCH_TIME_FOREVERDISPATCH_TIME_NOW表示当前;DISPATCH_TIME_FOREVER表示遥远的未来;一般可以直接设置timeout为这两个宏其中的一个,或者自己创建一个dispatch_time_t类型的变量。创建dispatch_time_t类型的变量有两种方法,dispatch_timedispatch_walltime。利用创建dispatch_time创建dispatch_time_t类型变量的时候一般也会用到这两个变量dispatch_time的声明如下:dispatch_time_t dispatch_time(dispatch_time_t when, int64_t delta);其参数when需传入一个dispatch_time_t类型的变量,和一个delta值。表示whendelta时间就是timeout的时间。例如:dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 1*1000*1000*1000);表示当前时间向后延时一秒为timeout的时间。
    • 关于信号量,一般可以用停车来比喻:
      停车场剩余4个车位,那么即使同时来了四辆车也能停的下。如果此时来了五辆车,那么就有一辆需要等待。信号量的值就相当于剩余车位的数目,dispatch_semaphore_wait函数就相当于来了一辆车,dispatch_semaphore_signal就相当于走了一辆车。停车位的剩余数目在初始化的时候就已经指明了(dispatch_semaphore_create(long value)),调用一次dispatch_semaphore_signal,剩余的车位就增加一个;调用一次dispatch_semaphore_wait剩余车位就减少一个;当剩余车位为0时,再来车(即调用dispatch_semaphore_wait)就只能等待。有可能同时有几辆车等待一个停车位。有些车主没有耐心,给自己设定了一段等待时间,这段时间内等不到停车位就走了,如果等到了就开进去停车。而有些车主就像把车停在这,所以就一直等下去。

    代码示例:

     1 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     2     dispatch_group_t group = dispatch_group_create();
     3     dispatch_semaphore_t sem = dispatch_semaphore_create(0);
     4     dispatch_group_async(group, queue, ^{
     5         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
     6             dispatch_semaphore_signal(sem);
     7             NSLog(@"请求一");
     8         });
     9         dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    10     });
    11     dispatch_group_async(group, queue, ^{
    12         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    13             dispatch_semaphore_signal(sem);
    14             NSLog(@"请求二");
    15         });
    16         dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    17     });
    18 
    19     dispatch_group_async(group, queue, ^{
    20         NSLog(@"请求三");
    21 
    22     });
    23     //在分组的所有任务完成后触发
    24     dispatch_group_notify(group, queue, ^{
    25 
    26         NSLog(@"请求完成");
    27     });

    打印结果:

    1 2017-02-17 09:47:58.084 信号量[54431:3002639] 请求三
    2 2017-02-17 09:47:58.958 信号量[54431:3002590] 请求二
    3 2017-02-17 09:47:59.177 信号量[54431:3002590] 请求一
    4 2017-02-17 09:47:59.177 信号量[54431:3002636] 请求完成

    就实现了我们的需求(多个网络请求都完成再做其他操作),当然也可以通过dispatch_group_enter、dispatch_group_leave结合实现,使用dispatch_group_enter()函数进入到任务组中,然后异步执行队列中的任务,最后使用dispatch_group_leave()函数离开任务组即可,

     1 dispatch_group_t group = dispatch_group_create();
     2     dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
     3 
     4     dispatch_group_enter(group);
     5     dispatch_group_async(group, queue, ^{
     6         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
     7             NSLog(@"任务1");
     8             dispatch_group_leave(group);
     9         });
    10     });
    11 
    12     dispatch_group_enter(group);
    13     dispatch_group_async(group, queue, ^{
    14         NSLog(@"任务2");
    15         dispatch_group_leave(group);
    16     });
    17 
    18     dispatch_group_enter(group);
    19     dispatch_group_async(group, queue, ^{
    20         NSLog(@"任务3");
    21         dispatch_group_leave(group);
    22     });
    23 
    24     dispatch_group_notify(group, queue, ^{
    25         NSLog(@"完成");
    26     });

    打印结果:

    1 2017-02-17 09:56:13.129 信号量[54888:3036359] 任务2
    2 2017-02-17 09:56:13.129 信号量[54888:3036359] 任务3
    3 2017-02-17 09:56:14.222 信号量[54888:3036264] 任务1
    4 2017-02-17 09:56:14.222 信号量[54888:3036359] 完成

    同样实现了我们需求

    顺序执行

    仍然通过信号量执行,执行任务一,将信号量dispatch_semaphore_wait减一,请求完成,再dispatch_semaphore_signal加一,一次类推,代码实现

     1 dispatch_semaphore_t sem = dispatch_semaphore_create(1);
     2     dispatch_queue_t queue = dispatch_queue_create("testBlock", NULL);
     3     dispatch_async(queue, ^{
     4         dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
     5         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
     6             NSLog(@"1");
     7             dispatch_semaphore_signal(sem);
     8 
     9         });
    10     });
    11     dispatch_async(queue, ^{
    12         dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    13         NSLog(@"2");
    14         dispatch_semaphore_signal(sem);
    15     });
    16     dispatch_async(queue, ^{
    17         dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    18         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    19             NSLog(@"3");
    20             dispatch_semaphore_signal(sem);
    21 
    22         });
    23     });
    24     dispatch_async(queue, ^{
    25         dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    26         NSLog(@"4");
    27         dispatch_semaphore_signal(sem);
    28 
    29     });

    打印结果:

    1 2017-02-17 09:57:54.058 信号量[54991:3043275] 1
    2 2017-02-17 09:57:54.059 信号量[54991:3043355] 2
    3 2017-02-17 09:57:57.358 信号量[54991:3043275] 3
    4 2017-02-17 09:57:57.358 信号量[54991:3043355] 4

    这篇文章对信号量做了详细介绍
    参考:
    关于dispatch_semaphore的使用



    作者:夜晚看日出
    链接:http://www.jianshu.com/p/56313152e87e
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    微人事项目-mybatis-持久层
    通过外键连接多个表
    springioc
    Redis 消息中间件 ServiceStack.Redis 轻量级
    深度数据对接 链接服务器 数据传输
    sqlserver 抓取所有执行语句 SQL语句分析 死锁 抓取
    sqlserver 索引优化 CPU占用过高 执行分析 服务器检查
    sql server 远程备份 bak 删除
    冒泡排序
    多线程 异步 beginInvoke EndInvoke 使用
  • 原文地址:https://www.cnblogs.com/SUPER-F/p/7365764.html
Copyright © 2011-2022 走看看