有时候我们会碰到这样子的一种情形:

同时获取两个网络请求的数据,但是网络请求是异步的,我们需要获取到两个网络请求的数据之后才能够进行下一步的操作,这个时候,就是线程组与信号量的用武之地了.

 #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信号量将异步线程转换为同步线程的更多相关文章

  1. iOS开发之线程组解决请求多个接口数据,完成后,再刷新界面

    1.多任务请求接口,完成后,在刷新数据,常用方法 2018年07月18日 16:34:38 hbblzjy 阅读数:1382 版权声明:本文为博主原创文章,未经博主允许不得转载. https://bl ...

  2. 0039 Java学习笔记-多线程-线程控制、线程组

    join线程 假如A线程要B线程去完成一项任务,在B线程完成返回之前,不进行下一步执行,那么就可以调用B线程的join()方法 join()方法的重载: join():等待不限时间 join(long ...

  3. Java多线程16:线程组

    线程组 可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式,如图所示: 线程组的作用是:可以批量管理线程或线程组对象,有效地对线 ...

  4. JAVA基础知识之多线程——线程组和未处理异常

    线程组 Java中的ThreadGroup类表示线程组,在创建新线程时,可以通过构造函数Thread(group...)来指定线程组. 线程组具有以下特征 如果没有显式指定线程组,则新线程属于默认线程 ...

  5. 【Java基础】Java多线程之线程组和线程池

    在上一篇文章中,讲述了线程的基本概念和用法,这里将继续讲述线程组和线程池的一些东西. 线程组:java.lang.ThreadGroup 1. 线程组的介绍 线程组表示一个线程的集合.此外,线程组也可 ...

  6. Linux线程 之 线程 线程组 进程 轻量级进程(LWP)

    Thread Local Storage,线程本地存储,大神Ulrich Drepper有篇PDF文档是讲TLS的,我曾经努力过三次尝试搞清楚TLS的原理,均没有彻底搞清楚.这一次是第三次,我沉浸gl ...

  7. Ultimate thread group线程组和Stepping thread group线程组测试场景

    Ultimate thread group线程组 当测试需求是要求进行波浪型的压力测试场景时,使用该线程组,例如:测试场景总共有10个线程,然后分为三个波段进行测试,每个波段负载策略设置为一样,如图:

  8. Java线程组(ThreadGroup)使用

    JDK 对线程组类注释: A thread group represents a set of threads. In addition, a thread group can also includ ...

  9. 线程组ThreadGroup分析详解 多线程中篇(三)

    线程组,顾名思义,就是线程的组,逻辑类似项目组,用于管理项目成员,线程组就是用来管理线程. 每个线程都会有一个线程组,如果没有设置将会有些默认的初始化设置 而在java中线程组则是使用类ThreadG ...

随机推荐

  1. 使用Microsoft Fakes进行单元测试(2)

    接上一篇使用Microsoft Fakes进行单元测试(1) 下面进行Shim的演示. 2.使用Shim替换静态方法 假设我们需要一个工具方法用来格式化当前时间为字符串,因为DateTime.Now一 ...

  2. 使用Python将HTML转成PDF

    主要使用的是wkhtmltopdf的Python封装--pdfkit 安装 1. Install python-pdfkit: $ pip install pdfkit 2. Install wkht ...

  3. ASP.NET MVC处理JsonResult返回时间DateTime问题

    在开发ASP.NET MVC时,如果你有使用jQuery的Ajax去获取一些json数据时,其中数据返回有包含日期时间的话,也许会觉得有点小问题. 现针对此问题,写一个小例子来演示一下,创建一个控制器 ...

  4. TCP 与 UDP

    TCP Transmission Control Protocol,传输控制协议,传输层通信协议. 采用“带重传的肯定确认”(Positive Acknowledge with Retransmiss ...

  5. iOS阶段学习第19天笔记(协议-Protocol)

    iOS学习(OC语言)知识点整理 一.关于协议(Protocol)的介绍 1)概念:协议指多个对象之间协商的一个接口对象,协议提供了一些方法用在协议的实现者和代理者      之间通讯的一种方式 2) ...

  6. Android中GPS定位的简单应用

    在Android中通过GPS获得当前位置,首先要获得一个LocationManager实例,通过该实例的getLastKnownLocation()方法获得第一个的位置,该方法的说明如下: void ...

  7. ArrayList和LinkedList的区别

    简单的说,ArrayList是顺序存储,而LinkedList是链式存储.

  8. 【Java每日一题】20161024

    20161021问题解析请点击今日问题下方的"[Java每日一题]20161024"查看 package Oct2016; public class Ques1024 { publ ...

  9. Redis数据库安装简介

    方法一:Redis手动cmd启动服务 端客户端的方法-------Redis 服务端测试使用1. 下载Redis(https://github.com/mythz/redis-windows)2. 解 ...

  10. AppleDoc的安裝使用

    前言,還是那句話,新手按照濤叔下面畫黃色的步驟順序執行就好了,不要問為什麼. 一.安裝(注意,濤叔事先已經下載了appledoc) 1.找到下載的appledoc目錄 $ cd /Users/libo ...