1.碰到的问题:
同时获取两个网络请求的数据,但是网络请求是异步的,我们需要获取到两个网络请求的数据之后才能够进行下一步的操作,这个时候,就是线程组与信号量的用武之地了.
执行的代码:
#import "ViewController.h"
2 #import <AFNetworking.h>
3
4
5 @interface ViewController ()
6
7 @end
8
9 @implementation ViewController
10
11 - (void)viewDidLoad {
12 [super viewDidLoad];
13 [self getNetworkingData];
14 }
15
16 - (void)getNetworkingData{
17 NSString *appIdKey = @"8781e4ef1c73ff20a180d3d7a42a8c04";
18 NSString* urlString_1 = @"http://api.openweathermap.org/data/2.5/weather";
19 NSString* urlString_2 = @"http://api.openweathermap.org/data/2.5/forecast/daily";
20 NSDictionary* dictionary =@{@"lat":@"40.04991291",
21 @"lon":@"116.25626162",
22 @"APPID" : appIdKey};
23 // 创建组
24 dispatch_group_t group = dispatch_group_create();
25 // 将第一个网络请求任务添加到组中
26 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
27 // 创建信号量
28 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
29 // 开始网络请求任务
30 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
31 [manager GET:urlString_1
32 parameters:dictionary
33 progress:nil
34 success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
35 NSLog(@"成功请求数据1:%@",[responseObject class]);
36 // 如果请求成功,发送信号量
37 dispatch_semaphore_signal(semaphore);
38 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
39 NSLog(@"失败请求数据");
40 // 如果请求失败,也发送信号量
41 dispatch_semaphore_signal(semaphore);
42 }];
43 // 在网络请求任务成功之前,信号量等待中
44 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
45 });
46 // 将第二个网络请求任务添加到组中
47 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
48 // 创建信号量
49 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
50 // 开始网络请求任务
51 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
52 [manager GET:urlString_2
53 parameters:dictionary
54 progress:nil
55 success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
56 NSLog(@"成功请求数据2:%@",[responseObject class]);
57 // 如果请求成功,发送信号量
58 dispatch_semaphore_signal(semaphore);
59 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
60 NSLog(@"失败请求数据");
61 // 如果请求失败,也发送信号量
62 dispatch_semaphore_signal(semaphore);
63 }];
64 // 在网络请求任务成功之前,信号量等待中
65 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
66 });
67 dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
68 NSLog(@"完成了网络请求,不管网络请求失败了还是成功了。");
69 });
70 }
71
72 @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] 完成了网络请求,不管网络请求失败了还是成功了。
3.总结:
网络请求然后处理响应数据是个耗时的操作,也是我们开发中常见的一种情形,在网络请求以及处理响应数据操作完毕之后我们在执行别的操作这样的过程也是我们开发中常见的情形。根据第三部分代码(没有使用信号量的代码)打印结果的顺序,我们可以知道,网络请求的任务是提交给子线程异步处理了,网络请求这样的任务也就快速执行完毕了,但是网络请求是一个任务,处理收到的网络响应又是一个任务,注意不要把这两个过程混为一谈。而收到网络响应以及处理返回响应的数据并不是在子线程中执行的,我们通过在回调响应处理的block(比如48~53行之间就有两个block)中打印当前线程,会发现回调响应处理的block是在主线程中被执行的。
如果读者很熟悉block回调这种通信机制的话,就不难理解,这个回调响应的block真正被调用执行的地方应该是AFN框架的底层代码,AFN 底层代码,是在获取到最终数据后,执行回调操作,此时,才能拿到相应数据,而这个处理网络响应的过程,AFN底层最终是这么做的:
4.简化总结:
信号量的使用前提是,想清楚你需要处理哪个线程等待,又要哪个线程继续执行,然后使用信号量。
比如上面的AFN网络请求的示例,block回调是在main主线程中执行的,而get请求是在自己创建的异步子线程中执行的。所以按照需求,就需要自己创建的异步子线程等待main主线程中的block执行完了之后再执行。所以异步子线程需要信号量wait,main主线程就设置signal发送信号量