GCD全解-dispatch-多个网络请求处理

简介: 在开发中,我们会经常遇到这样的需求,需要我们同时做多个网络请求,然后架构所需数据,再统一渲染页面。

1、问题


在开发中,我们会经常遇到这样的需求,需要我们同时做多个网络请求,然后架构所需数据,再统一渲染页面。


2、解决方案


2.1、一般方式:不做任何处理连续请求多个协议

viewDidLoad添加按钮:


//1.无处理
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 100, 100, 40);
    btn1.backgroundColor = [UIColor grayColor];
    [btn1 setTitle:@"Respective" forState:UIControlStateNormal];
    [btn1 addTarget:self action:@selector(btn1Action) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn1];


实现:


//1.Respective
-(void)btn1Action{
    NSString *str = @"https://blog.csdn.net/weixin_38633659";
    NSURL *url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    for (int i=0; i<10; i++) {
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSLog(@"%d---%d",i,i);
        }];
        [task resume];
    }
    NSLog(@"end");
}


控制台输出:

2021-02-24 10:20:10.503643+0800 TestDemo[18587:9715251] end
2021-02-24 10:20:10.676472+0800 TestDemo[18587:9715251] 0---0
2021-02-24 10:20:10.704834+0800 TestDemo[18587:9715251] 1---1
2021-02-24 10:20:10.754361+0800 TestDemo[18587:9715251] 4---4
2021-02-24 10:20:10.760842+0800 TestDemo[18587:9715251] 2---2
2021-02-24 10:20:10.800468+0800 TestDemo[18587:9715251] 5---5
2021-02-24 10:20:10.840135+0800 TestDemo[18587:9715251] 7---7
2021-02-24 10:20:10.844221+0800 TestDemo[18587:9715251] 6---6
2021-02-24 10:20:10.846853+0800 TestDemo[18587:9715251] 3---3
2021-02-24 10:20:10.888963+0800 TestDemo[18587:9715251] 8---8
2021-02-24 10:20:10.945394+0800 TestDemo[18587:9715251] 9---9


分析:

1、无任何处理情况下,end最先被打印出来。

2、由于请求是异步的,然后各个网络请求的回调顺序是无序的,无法监听最后一次结束,进行回调后的统一处理。

3、该场景适合协议和后续处理互相独立的,没有影响的。


应用场景:

UI格式固定,只需要更新服务器下发数据,这时候就可以用各自渲染实现,互不影响。


2.2、GCD队列组:dispatch_group_t

viewDidLoad添加按钮:


//2.GCD队列组
    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn2.frame = CGRectMake(100, 100, 120, 40);
    btn2.backgroundColor = [UIColor grayColor];
    [btn2 setTitle:@"group--" forState:UIControlStateNormal];
    [btn2 addTarget:self action:@selector(btn2Action) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:btn2];


实现:


/// 2.GCD队列组
- (void)btn2Action {
    NSString *str = @"https://blog.csdn.net/weixin_38633659";
    NSURL *url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    __block NSMutableArray *responseDataMarr = [NSMutableArray arrayWithCapacity:0];
    dispatch_group_t downloadGroup = dispatch_group_create();
    for (NSInteger i = 0; i < 10; i ++) {
        dispatch_group_enter(downloadGroup);
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            [responseDataMarr addObject:[NSString stringWithFormat:@"%ld:%@",i,response.URL.absoluteString]];
            NSLog(@"%ld------------group",i);
            dispatch_group_leave(downloadGroup);
        }];
        [task resume];
    }
    dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER);
    dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
        NSLog(@"end----------%@",responseDataMarr);
    });
}


控制台输出1:


2021-02-24 10:24:36.104843+0800 TestDemo[18587:9712251] 4------------group
2021-02-24 10:24:36.109190+0800 TestDemo[18587:9712251] 8------------group
2021-02-24 10:24:36.160072+0800 TestDemo[18587:9712688] 2------------group
2021-02-24 10:24:36.217361+0800 TestDemo[18587:9712690] 0------------group
2021-02-24 10:24:36.263296+0800 TestDemo[18587:9712183] 6------------group
2021-02-24 10:24:36.275118+0800 TestDemo[18587:9712251] 1------------group
2021-02-24 10:24:36.320592+0800 TestDemo[18587:9712251] 9------------group
2021-02-24 10:24:36.377655+0800 TestDemo[18587:9712688] 7------------group
2021-02-24 10:24:36.385682+0800 TestDemo[18587:9712183] 3------------group
2021-02-24 10:24:36.422906+0800 TestDemo[18587:9712183] 5------------group
2021-02-24 10:24:36.425515+0800 TestDemo[18587:9711955] end----------(
    "4:https://blog.csdn.net/weixin_38633659",
    "8:https://blog.csdn.net/weixin_38633659",
    "2:https://blog.csdn.net/weixin_38633659",
    "0:https://blog.csdn.net/weixin_38633659",
    "6:https://blog.csdn.net/weixin_38633659",
    "1:https://blog.csdn.net/weixin_38633659",
    "9:https://blog.csdn.net/weixin_38633659",
    "7:https://blog.csdn.net/weixin_38633659",
    "3:https://blog.csdn.net/weixin_38633659",
    "5:https://blog.csdn.net/weixin_38633659"


控制台输出2:

2021-02-24 10:27:51.144579+0800 TestDemo[18587:9713318] 4------------group
2021-02-24 10:27:51.201340+0800 TestDemo[18587:9713361] 3------------group
2021-02-24 10:27:51.241146+0800 TestDemo[18587:9713359] 6------------group
2021-02-24 10:27:51.249819+0800 TestDemo[18587:9713361] 9------------group
2021-02-24 10:27:51.254865+0800 TestDemo[18587:9713361] 1------------group
2021-02-24 10:27:51.259506+0800 TestDemo[18587:9713361] 0------------group
2021-02-24 10:27:51.269500+0800 TestDemo[18587:9713360] 7------------group
2021-02-24 10:27:51.289864+0800 TestDemo[18587:9713263] 2------------group
2021-02-24 10:27:51.303468+0800 TestDemo[18587:9713263] 8------------group
2021-02-24 10:27:51.307422+0800 TestDemo[18587:9713318] 5------------group
2021-02-24 10:27:51.308330+0800 TestDemo[18587:9711955] end----------(
    "4:https://blog.csdn.net/weixin_38633659",
    "3:https://blog.csdn.net/weixin_38633659",
    "6:https://blog.csdn.net/weixin_38633659",
    "9:https://blog.csdn.net/weixin_38633659",
    "1:https://blog.csdn.net/weixin_38633659",
    "0:https://blog.csdn.net/weixin_38633659",
    "7:https://blog.csdn.net/weixin_38633659",
    "2:https://blog.csdn.net/weixin_38633659",
    "8:https://blog.csdn.net/weixin_38633659",
    "5:https://blog.csdn.net/weixin_38633659"
)


分析:

1、从上两次输出可以看出,end都是在所有网络请求之后才打印出来。

2、协议都是异步处理,互相独立,只有等全部协议请求完成之后,再统一处理


适用场景:

这种情况适合前后协议请求没有绝对的逻辑要求,而是对最后请求返回的数据需要统一整理,再渲染数据。


原理:

代码中我们只添加了4行代码


dispatch_group_t downloadGroup = dispatch_group_create();
dispatch_group_enter(downloadGroup);
dispatch_group_leave(downloadGroup);
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
    // 统一返回处理
});


1、dispatch_group_create:创建一个group,初始计数为0;

2、dispatch_group_enter:负责将group的计数+1;

3、dispatch_group_leave:负责将group的计数-1;

4、dispatch_group_notify:监听group计数,当group计数为0时,执行里面的操作。


2.3、GCD信号量:dispatch_semaphore_t

viewDidLoad添加按钮:


//3.semaphore
UIButton *btn3 = [UIButton buttonWithType:UIButtonTypeCustom];
btn3.frame = CGRectMake(100, 200, 120, 40);
btn3.backgroundColor = [UIColor grayColor];
[btn3 setTitle:@"semaphore" forState:UIControlStateNormal];
[btn3 addTarget:self action:@selector(btn3Action) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn3];


实现:


/// 3.semaphore--
- (void)btn3Action {
    NSString *str = @"https://blog.csdn.net/weixin_38633659";
    NSURL *url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    __block NSMutableArray *responseDataMarr = [NSMutableArray arrayWithCapacity:0];
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    for (NSInteger i = 0; i < 10; i ++) {
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            [responseDataMarr addObject:[NSString stringWithFormat:@"%ld:%@",i,response.URL.absoluteString]];
            dispatch_semaphore_signal(sem);
            NSLog(@"%ld------------semaphore",i);
        }];
        [task resume];
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"end----------%@",responseDataMarr);
    });
}


控制台输出:


2021-02-24 10:24:09.656653+0800 TestDemo[18587:9712527] 0------------semaphore
2021-02-24 10:24:09.918128+0800 TestDemo[18587:9712251] 1------------semaphore
2021-02-24 10:24:10.129609+0800 TestDemo[18587:9712251] 2------------semaphore
2021-02-24 10:24:10.364854+0800 TestDemo[18587:9712251] 3------------semaphore
2021-02-24 10:24:10.562344+0800 TestDemo[18587:9712251] 4------------semaphore
2021-02-24 10:24:10.767919+0800 TestDemo[18587:9712251] 5------------semaphore
2021-02-24 10:24:10.987184+0800 TestDemo[18587:9712527] 6------------semaphore
2021-02-24 10:24:11.221210+0800 TestDemo[18587:9712183] 7------------semaphore
2021-02-24 10:24:11.464752+0800 TestDemo[18587:9712527] 8------------semaphore
2021-02-24 10:24:11.690382+0800 TestDemo[18587:9712369] 9------------semaphore
2021-02-24 10:24:15.755117+0800 TestDemo[18587:9711955] end----------(
    "0:https://blog.csdn.net/weixin_38633659",
    "1:https://blog.csdn.net/weixin_38633659",
    "2:https://blog.csdn.net/weixin_38633659",
    "3:https://blog.csdn.net/weixin_38633659",
    "4:https://blog.csdn.net/weixin_38633659",
    "5:https://blog.csdn.net/weixin_38633659",
    "6:https://blog.csdn.net/weixin_38633659",
    "7:https://blog.csdn.net/weixin_38633659",
    "8:https://blog.csdn.net/weixin_38633659",
    "9:https://blog.csdn.net/weixin_38633659"
)


分析:

end打印结果也是在最后的,而且协议请求插入顺序与结果返回顺序是串行的。


适用场景:

资源上传:图片数组资源返回的链接与上传有严格的顺序关系。


原理:

代码中我们只添加了3行代码


dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_semaphore_signal(sem);
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);


dispatch_semaphore_create   创建一个semaphore

dispatch_semaphore_signal   信号量+1,如果前一个值小于0,这个函数在返回之前唤醒一个等待的线程。(发送一个信号 )

dispatch_semaphore_wait    信号量-1。如果结果值小于0,则该函数在返回之前等待一个信号。(等待信号)


如何在GCD中快速的控制并发呢?


信号量是一个整形值并且具有一个初始计数值,并且支持两个操作:信号通知和等待。

当一个信号量被信号通知,其计数会被增加。当一个线程在一个信号量上等待时,线程会被阻塞(如果有必要的话),直至计数器大于零,然后线程会减少这个计数。


2.4、GCD信号量实现GCD队列组功能(不推荐)

这里还有一种情况,还是使用信号量semaphore,轮询计数,等待全部回调完成之后,增加一个信号量标记。可以实现队列组dispatch_group_t一样的效果。


/// semaphore:等待所有异步请求完成之后,再添加处理信号量
- (void)btn4Action {
    NSString *str = @"https://blog.csdn.net/weixin_38633659";
    NSURL *url = [NSURL URLWithString:str];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSession *session = [NSURLSession sharedSession];
    dispatch_semaphore_t sem = dispatch_semaphore_create(0);
    __block NSInteger count = 0;
    for (int i=0; i<10; i++) {
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSLog(@"%d---%d",i,i);
            count++;
            if (count==10) {
                dispatch_semaphore_signal(sem);
                count = 0;
            }
        }];
        [task resume];
    }
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"end");
    });
}

我们可以知道有这个用法,但是不推荐使用这种方法,当需要串行返回的使用信号量实现,而可以并列实现的使用队列组实现。养成一个良好的习惯。


额外拓展:

顺便看一下下面的拓展,也是个很有意思的知识点:

3、iOS开发:深入理解GCD 第二篇(dispatch_group、dispatch_barrier、基于线程安全的多读单写):https://www.cnblogs.com/ziyi–caolu/p/4900650.html

https://blog.csdn.net/lfdanding/article/details/100024022


相关文章
|
3月前
|
API 调度 iOS开发
多线程和异步编程:什么是 GCD(Grand Central Dispatch)?如何在 iOS 中使用 GCD?
多线程和异步编程:什么是 GCD(Grand Central Dispatch)?如何在 iOS 中使用 GCD?
28 1
|
iOS开发
iOS多线程的初步研究-- dispatch同步
GCD提供两种方式支持dispatch队列同步,即dispatch组和信号量。
147 0
|
前端开发 Java 开发者
Dispatch 设计| 学习笔记
快速学习 Dispatch 设计。
Dispatch 设计| 学习笔记
GCD全解-dispatch_after/dispatch_time-t延迟操作
GCD全解-dispatch_after/dispatch_time-t延迟操作
259 0
|
安全 调度
GCD全解-dispatch_once-创建单例
单例是一种类,该类只在第一次用的时候实例化一个对象,后期直接调用此对象。 在Foundation框架中比如NSFileManger和NSNotificationCenter,分别通过它们的类方法defaultManager和defaultCenter获取。尽管不是严格意义的单例,这些类方法返回一个可以在应用的所有代码中访问到的类的共享实例。
127 0
GCD全解-dispatch_queue-队列创建
GCD全解-dispatch_queue-队列创建
257 0
GCD全解-dispatch_barrier_sync/async-栅栏函数
GCD全解-dispatch_barrier_sync/async-栅栏函数
242 0
|
前端开发 Java 开发者
Dispatch设计|学习笔记
快速学习Dispatch设计
Dispatch设计|学习笔记
|
安全 应用服务中间件 Linux
Envoy源码分析之Dispatcher机制
# Dispatcher机制 ​ Envoy和Nginx一样都是基于事件驱动的架构,这种架构的核心就是事件循环(EventLoop)。业界目前典型的几种事件循环实现主要有Libevent、Libev、Libuv、Boost.Asio等,也可以完全基于Linux系统调用epoll来实现。Envoy选择在Libevent的基础上进行了封装,实现了自己的事件循环机制,在Envoy中被称为`Disp
1599 2
|
JavaScript 中间件
Fish Redux中的Dispatch是怎么实现的?
作者:闲鱼技术-卢克 零.前言 我们在使用fish-redux构建应用的时候,界面代码(view)和事件的处理逻辑(reducer,effect)是完全解耦的,界面需要处理事件的时候将action分发给对应的事件处理逻辑去进行处理,而这个分发的过程就是下面要讲的dispatch, 通过本篇的内容,你可以更深刻的理解一个action是如何一步步去进行分发的。
2627 0