有时候咱们会碰到这样子的一种情形:html
同时获取两个网络请求的数据,可是网络请求是异步的,咱们须要获取到两个网络请求的数据以后才可以进行下一步的操做,这个时候,就是线程组与信号量的用武之地了.api
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] 完成了网络请求,无论网络请求失败了仍是成功了。网络
为了和上面造成对比,我特意将全部的信号量的代码所有去除,可是保留GCD线程组的使用,而后运行看打印结果。app
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 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; 29 [manager GET:urlString_1 30 parameters:dictionary 31 progress:nil 32 success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { 33 NSLog(@"成功请求数据1:%@",[responseObject class]); 34 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 35 NSLog(@"失败请求数据"); 36 }]; 37 }); 38 // 将第二个网络请求任务添加到组中 39 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 40 // 开始网络请求任务 41 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; 42 [manager GET:urlString_2 43 parameters:dictionary 44 progress:nil 45 success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { 46 NSLog(@"成功请求数据2:%@",[responseObject class]); 47 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 48 NSLog(@"失败请求数据"); 49 }]; 50 }); 51 dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 52 NSLog(@"完成了网络请求,无论网络请求失败了仍是成功了。"); 53 }); 54 } 55 56 @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不是会在组中的异步任务执行完毕了才会执行么?这是什么状况?async
下面我在上面的代码基础上添加了第3三、3八、3九、4九、5四、55行代码(也就是下面红色高亮的几行代码,都是打印当前线程),而后咱们再来看看打印结果:url
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 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; 29 [manager GET:urlString_1 30 parameters:dictionary 31 progress:nil 32 success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { 33 NSLog(@"%@",[NSThread currentThread]); 34 NSLog(@"成功请求数据1:%@",[responseObject class]); 35 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 36 NSLog(@"失败请求数据"); 37 }]; 38 NSLog(@"%@",[NSThread currentThread]); 39 NSLog(@"AFN网络请求框架请求完毕"); 40 }); 41 // 将第二个网络请求任务添加到组中 42 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 43 // 开始网络请求任务 44 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; 45 [manager GET:urlString_2 46 parameters:dictionary 47 progress:nil 48 success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { 49 NSLog(@"%@",[NSThread currentThread]); 50 NSLog(@"成功请求数据2:%@",[responseObject class]); 51 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { 52 NSLog(@"失败请求数据"); 53 }]; 54 NSLog(@"%@",[NSThread currentThread]); 55 NSLog(@"AFN网络请求框架请求完毕"); 56 }); 57 dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 58 NSLog(@"完成了网络请求,无论网络请求失败了仍是成功了。"); 59 }); 60 } 61 62 @end
打印结果(舒适提示:请求数据可能会出现失败,由于这个网络请求的url是国外的服务器,可是不要紧,不要在乎这个细节,打印顺序仍是同样的):spa
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发送信号量。