用GCD线程组与GCD信号量将异步线程转换为同步线程
有时候我们会碰到这样子的一种情形:
同时获取两个网络请求的数据,但是网络请求是异步的,我们需要获取到两个网络请求的数据之后才能够进行下一步的操作,这个时候,就是线程组与信号量的用武之地了.
#import "ViewController.h"
#import <AFNetworking.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
[self getNetworkingData];
} - (void)getNetworkingData{
NSString *appIdKey = @"8781e4ef1c73ff20a180d3d7a42a8c04";
NSString* urlString_1 = @"http://api.openweathermap.org/data/2.5/weather";
NSString* urlString_2 = @"http://api.openweathermap.org/data/2.5/forecast/daily";
NSDictionary* dictionary =@{@"lat":@"40.04991291",
@"lon":@"116.25626162",
@"APPID" : appIdKey};
// 创建组
dispatch_group_t group = dispatch_group_create();
// 将第一个网络请求任务添加到组中
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create();
// 开始网络请求任务
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:urlString_1
parameters:dictionary
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"成功请求数据1:%@",[responseObject class]);
// 如果请求成功,发送信号量
dispatch_semaphore_signal(semaphore);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败请求数据");
// 如果请求失败,也发送信号量
dispatch_semaphore_signal(semaphore);
}];
// 在网络请求任务成功之前,信号量等待中
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
// 将第二个网络请求任务添加到组中
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create();
// 开始网络请求任务
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:urlString_2
parameters:dictionary
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"成功请求数据2:%@",[responseObject class]);
// 如果请求成功,发送信号量
dispatch_semaphore_signal(semaphore);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败请求数据");
// 如果请求失败,也发送信号量
dispatch_semaphore_signal(semaphore);
}];
// 在网络请求任务成功之前,信号量等待中
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
NSLog(@"完成了网络请求,不管网络请求失败了还是成功了。");
});
} @end
打印结果:
2016-03-15 04:01:53.279 NetWorking[83611:1508240] 成功请求数据1:__NSCFDictionary
2016-03-15 04:01:53.280 NetWorking[83611:1508240] 成功请求数据2:__NSCFDictionary
2016-03-15 04:01:53.281 NetWorking[83611:1508287] 完成了网络请求,不管网络请求失败了还是成功了。
为了和上面形成对比,我特地将所有的信号量的代码全部去除,但是保留GCD线程组的使用,然后运行看打印结果。
#import "ViewController.h"
#import <AFNetworking.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
[self getNetworkingData];
} - (void)getNetworkingData{
NSString *appIdKey = @"8781e4ef1c73ff20a180d3d7a42a8c04";
NSString* urlString_1 = @"http://api.openweathermap.org/data/2.5/weather";
NSString* urlString_2 = @"http://api.openweathermap.org/data/2.5/forecast/daily";
NSDictionary* dictionary =@{@"lat":@"40.04991291",
@"lon":@"116.25626162",
@"APPID" : appIdKey};
// 创建组
dispatch_group_t group = dispatch_group_create();
// 将第一个网络请求任务添加到组中
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 开始网络请求任务
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:urlString_1
parameters:dictionary
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"成功请求数据1:%@",[responseObject class]);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败请求数据");
}];
});
// 将第二个网络请求任务添加到组中
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 开始网络请求任务
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:urlString_2
parameters:dictionary
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"成功请求数据2:%@",[responseObject class]);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败请求数据");
}];
});
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
NSLog(@"完成了网络请求,不管网络请求失败了还是成功了。");
});
} @end
打印结果:
2016-03-15 04:05:09.378 NetWorking[83698:1510242] 完成了网络请求,不管网络请求失败了还是成功了。
2016-03-15 04:05:10.185 NetWorking[83698:1510096] 成功请求数据1:__NSCFDictionary
2016-03-15 04:05:10.186 NetWorking[83698:1510096] 成功请求数据2:__NSCFDictionary
看到这个打印结果,我们似乎有点看不懂了,难道notify线程组没用了?notify不是会在组中的异步任务执行完毕了才会执行么?这是什么情况?
下面我在上面的代码基础上添加了第33、38、39、49、54、55行代码(也就是下面红色高亮的几行代码,都是打印当前线程),然后我们再来看看打印结果:
#import "ViewController.h"
#import <AFNetworking.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad];
[self getNetworkingData];
} - (void)getNetworkingData{
NSString *appIdKey = @"8781e4ef1c73ff20a180d3d7a42a8c04";
NSString* urlString_1 = @"http://api.openweathermap.org/data/2.5/weather";
NSString* urlString_2 = @"http://api.openweathermap.org/data/2.5/forecast/daily";
NSDictionary* dictionary =@{@"lat":@"40.04991291",
@"lon":@"116.25626162",
@"APPID" : appIdKey};
// 创建组
dispatch_group_t group = dispatch_group_create();
// 将第一个网络请求任务添加到组中
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 开始网络请求任务
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:urlString_1
parameters:dictionary
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"成功请求数据1:%@",[responseObject class]);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败请求数据");
}];
NSLog(@"%@",[NSThread currentThread]);
39 NSLog(@"AFN网络请求框架请求完毕");
});
// 将第二个网络请求任务添加到组中
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
// 开始网络请求任务
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:urlString_2
parameters:dictionary
progress:nil
success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",[NSThread currentThread]);
NSLog(@"成功请求数据2:%@",[responseObject class]);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"失败请求数据");
}];
NSLog(@"%@",[NSThread currentThread]);
55 NSLog(@"AFN网络请求框架请求完毕");
});
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{
NSLog(@"完成了网络请求,不管网络请求失败了还是成功了。");
});
} @end
打印结果(温馨提示:请求数据可能会出现失败,因为这个网络请求的url是国外的服务器,但是没关系,不要在意这个细节,打印顺序还是一样的):
2016-03-15 04:30:07.406 NetWorking[84306:1523047] <NSThread: 0x7fc258725e10>{number = 2, name = (null)}
2016-03-15 04:30:07.406 NetWorking[84306:1523048] <NSThread: 0x7fc258406100>{number = 3, name = (null)}
2016-03-15 04:30:07.407 NetWorking[84306:1523047] AFN网络请求框架请求完毕
2016-03-15 04:30:07.407 NetWorking[84306:1523048] AFN网络请求框架请求完毕
2016-03-15 04:30:07.407 NetWorking[84306:1523075] 完成了网络请求,不管网络请求失败了还是成功了。
2016-03-15 04:30:08.239 NetWorking[84306:1523016] <NSThread: 0x7fc258507af0>{number = 1, name = main}
2016-03-15 04:30:08.239 NetWorking[84306:1523016] 成功请求数据1:__NSCFDictionary
2016-03-15 04:30:08.240 NetWorking[84306:1523016] <NSThread: 0x7fc258507af0>{number = 1, name = main}
2016-03-15 04:30:08.240 NetWorking[84306:1523016] 成功请求数据2:__NSCFDictionary
总结:网络请求然后处理响应数据是个耗时的操作,也是我们开发中常见的一种情形,在网络请求以及处理响应数据操作完毕之后我们在执行别的操作这样的过程也是我们开发中常见的情形。根据第三部分代码(没有使用信号量的代码)打印结果的顺序,我们可以知道,网络请求的任务是提交给子线程异步处理了,网络请求这样的任务也就快速执行完毕了,但是网络请求是一个任务,处理收到的网络响应又是一个任务,注意不要把这两个过程混为一谈。而收到网络响应以及处理返回响应的数据并不是在子线程中执行的,我们通过在回调响应处理的block(比如48~53行之间就有两个block)中打印当前线程,会发现回调响应处理的block是在主线程中被执行的。
如果读者很熟悉block回调这种通信机制的话,就不难理解,这个回调响应的block真正被调用执行的地方应该是AFN框架的底层代码,AFN 底层代码,是在获取到最终数据后,执行回调操作,此时,才能拿到相应数据,而这个处理网络响应的过程,AFN底层最终是这么做的:
也就是说,seccess和failure都是在主线中异步任务中执行的。
那么,这时候,如果我们需要确定这个主线程中收到网络响应的数据被处理操作结束之后,才最后执行我们需要最后的操作的话,仅仅依靠线程组看来是不够的,所以很少用到的GCD信号量就有了用武之地了。
当然,以上代码如果不用GCD线程组,只用GCD的信号量来处理,也是可以的,这个就留给大家自己探究吧。
最后再简化总结一下:信号量的使用前提是,想清楚你需要处理哪个线程等待,又要哪个线程继续执行,然后使用信号量。
比如上面的AFN网络请求的示例,block回调是在main主线程中执行的,而get请求是在自己创建的异步子线程中执行的。所以按照需求,就需要自己创建的异步子线程等待main主线程中的block执行完了之后再执行。所以异步子线程需要信号量wait,main主线程就设置signal发送信号量。
转载注明出处:http://www.cnblogs.com/goodboy-heyang/p/5277910.html
用GCD线程组与GCD信号量将异步线程转换为同步线程的更多相关文章
- iOS开发之线程组解决请求多个接口数据,完成后,再刷新界面
1.多任务请求接口,完成后,在刷新数据,常用方法 2018年07月18日 16:34:38 hbblzjy 阅读数:1382 版权声明:本文为博主原创文章,未经博主允许不得转载. https://bl ...
- 0039 Java学习笔记-多线程-线程控制、线程组
join线程 假如A线程要B线程去完成一项任务,在B线程完成返回之前,不进行下一步执行,那么就可以调用B线程的join()方法 join()方法的重载: join():等待不限时间 join(long ...
- Java多线程16:线程组
线程组 可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式,如图所示: 线程组的作用是:可以批量管理线程或线程组对象,有效地对线 ...
- JAVA基础知识之多线程——线程组和未处理异常
线程组 Java中的ThreadGroup类表示线程组,在创建新线程时,可以通过构造函数Thread(group...)来指定线程组. 线程组具有以下特征 如果没有显式指定线程组,则新线程属于默认线程 ...
- 【Java基础】Java多线程之线程组和线程池
在上一篇文章中,讲述了线程的基本概念和用法,这里将继续讲述线程组和线程池的一些东西. 线程组:java.lang.ThreadGroup 1. 线程组的介绍 线程组表示一个线程的集合.此外,线程组也可 ...
- Linux线程 之 线程 线程组 进程 轻量级进程(LWP)
Thread Local Storage,线程本地存储,大神Ulrich Drepper有篇PDF文档是讲TLS的,我曾经努力过三次尝试搞清楚TLS的原理,均没有彻底搞清楚.这一次是第三次,我沉浸gl ...
- Ultimate thread group线程组和Stepping thread group线程组测试场景
Ultimate thread group线程组 当测试需求是要求进行波浪型的压力测试场景时,使用该线程组,例如:测试场景总共有10个线程,然后分为三个波段进行测试,每个波段负载策略设置为一样,如图:
- Java线程组(ThreadGroup)使用
JDK 对线程组类注释: A thread group represents a set of threads. In addition, a thread group can also includ ...
- 线程组ThreadGroup分析详解 多线程中篇(三)
线程组,顾名思义,就是线程的组,逻辑类似项目组,用于管理项目成员,线程组就是用来管理线程. 每个线程都会有一个线程组,如果没有设置将会有些默认的初始化设置 而在java中线程组则是使用类ThreadG ...
随机推荐
- 我的vim配置文件
强烈拥护开源精神,高举开源大旗,今天我就分享下我自己结合网上还有自己实际使用配的vimrc,可以给各位参考下,不要见笑,具体说明我在rc里写的也很详细,可以具体看下,也希望可以借这个机会能多认识认识几 ...
- img[src*="same"]{}
假如你有一组图片,你想选择其中一些名字中带有same的图片,那么你就可以用这种写法,这里的意思就是选择所有正确路径下名字中带有same的图片文件. 譬如说:me_same.png,you_same.p ...
- 显示XML文档时排序数据
先看XML文档: 也可拷贝下面代码另存为XMl文档: <stepList> <steps> <step> <order>1</order> ...
- 【Java每日一题】20161208
package Dec2016; import java.util.List; public class Ques1208 { public static void add(List<? ext ...
- mybaits注解
基本的增删改 @Insert("insert into t_user values(null,#{username},#{password},#{account})") @Dele ...
- python处理ha文件
ha.txt文件内容: frontend oldboy.org bind 0.0.0.0:80 option httplog option httpclose option forwardfor lo ...
- MAC安裝CocoaPods
前言,鑒於很多人都比較趕時間,有人是為了去應付工作,有人是為了去找妹子,總之,不管你懂不懂,如果你只是想安裝一下,那麼你只需要按照濤叔下面畫黃色的命令複製粘貼到終端順序執行就好了. 一.安裝 1.Ru ...
- Java经典实例:使用正则表达式:测试模式
import java.util.regex.Pattern; /** * Created by Frank * 在Java中使用正则表达式:测试模式 */ public class RESimple ...
- gcc shared object
介绍一个生成动态链接库*.so的例子: 首先新建1个头文件test.h: #include <stdio.h> void first(); void second(); void thir ...
- java集合-ArrayList
一.ArrayList 概述 ArrayList 是实现 List 接口的动态数组,所谓动态就是它的大小是可变的.实现了所有可选列表操作,并允许包括 null 在内的所有元素.除了实现 List 接口 ...