本文是GCD多线程编程中dispatch_group
内容的小结,经过本文,你能够了解到:ios
dispatch_group
来实如今一系列并发任务完成后作一些收尾工做的需求咱们在日常的开发中,常常会遇到这样这样的一个需求,当应用程序启动时,须要从服务器获取各类配置信息,而后再去作首页UI的初始化与后面的逻辑处理。对于这个需求,咱们确定是但愿能够调用一个方法来执行这些任务,并在全部网络请求完成后调用已完成的回调,用于后续UI的的初始化。git
面对这种场景,咱们可使用一种最直接的方式,就是从第一个网络请求的回调中继续下一个网络请求,可是这样实现的话,咱们的代码就会像这种:github
}];
}];
}];
}];
}];
复制代码
这种方式虽然也一样能够实现需求,可是在编码上不够优雅,没有太强的阅读性,其实,这种须要等待一系列并发任务完成,而后在完成以后作一些收尾的工做的需求,GCD已经提供了一组API,就是使用dispatch_group
来作,dispatch_group
的使用分为如下几步:macos
dispatch_group
组接下来,咱们来具体看看dispatch_group
相关的API与基本使用编程
测试代码在这bash
/*! * @typedef dispatch_group_t * @abstract * A group of blocks submitted to queues for asynchronous invocation. */
DISPATCH_DECL(dispatch_group);
复制代码
/*! * @function dispatch_group_create * * @abstract * Creates new group with which blocks may be associated. * * @discussion * This function creates a new group with which blocks may be associated. * The dispatch group may be used to wait for the completion of the blocks it * references. The group object memory is freed with dispatch_release(). * * @result * The newly created group, or NULL on failure. */
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_MALLOC DISPATCH_RETURNS_RETAINED DISPATCH_WARN_RESULT
DISPATCH_NOTHROW
dispatch_group_t
dispatch_group_create(void);
复制代码
dispatch_group_t
其实就是提交到队列中用以进行异步调用的一组任务服务器
咱们可使用 dispatch_group_create
方法来建立group网络
dispatch_group_t group = dispatch_group_create();
复制代码
添加任务有2种方式:session
dispatch_group_async
添加任务到一个特定的队列group
,咱们开始了一个任务(dispatch_group_enter
),或者任务结束了(dispatch_group_leave
)#ifdef __BLOCKS__
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_group_async(dispatch_group_t group,
dispatch_queue_t queue,
dispatch_block_t block);
#endif /* __BLOCKS__ */
复制代码
这个函数有3个参数,第一个是管理这些异步任务的group,第二个是用于提交异步任务队列,第三个是咱们提交的任务。多线程
使用这个方法,咱们能够定制咱们的网络请求任务,添加到对应的并发队列,而后使用group管理这些任务,这样咱们的异步并发网络请求的目的就实现了。
可是,咱们平时的开发过程当中,咱们的网络请求基本上都是须要异步添加任务的,没法直接使用队列,这时咱们就可使用dispatch_group_enter与
dispatch_group_leave`这一对API
/*!
* @function dispatch_group_enter
*
* @abstract
* Manually indicate a block has entered the group
*
* @discussion
* Calling this function indicates another block has joined the group through
* a means other than dispatch_group_async(). Calls to this function must be
* balanced with dispatch_group_leave().
*
* @param group
* The dispatch group to update.
* The result of passing NULL in this parameter is undefined.
*/
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_group_enter(dispatch_group_t group);
/*!
* @function dispatch_group_leave
*
* @abstract
* Manually indicate a block in the group has completed
*
* @discussion
* Calling this function indicates block has completed and left the dispatch
* group by a means other than dispatch_group_async().
*
* @param group
* The dispatch group to update.
* The result of passing NULL in this parameter is undefined.
*/
API_AVAILABLE(macos(10.6), ios(4.0))
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_group_leave(dispatch_group_t group);
复制代码
dispatch_group_enter
表示这个任务已经添加到group中
dispatch_group_leave
表示添加到group中的这个任务已经执行完成
咱们常常会使用这组API,将一些异步的网络请求的任务包装起来放进group中(早版本的AFNetworking中执行异步任务):
NSLog(@"使用dispatch_group_enter方式追加任务3");
dispatch_group_enter(self.group);
//开启一个网络请求
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url =
[NSURL URLWithString:[@"https://www.baidu.com/" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"GET";
NSLog(@"3---start---%@",[NSThread currentThread]);
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"%@", [error localizedDescription]);
}
if (data) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%@", dict);
}
NSLog(@"3---end---%@",[NSThread currentThread]);
dispatch_group_leave(self.group);
}];
[dataTask resume];
复制代码
这组API必须配对调用,不然,group中任务执行完成的指令永远不会调用。
这里也有2种方式,dispatch_group_wait
与dispatch_group_notify
这种方式会阻塞当前的线程,直到group中的任务所有完成,程序才会继续往下执行。
这种方式是添加一个异步执行的任务做为结束任务,当group中的任务所有完成,才会执行dispatch_group_notify
中添加的异步任务,这种方式不会阻塞当前线程,同时有一个单独的异步回调,代码组织性更好,使用也更新普遍一些。
接下来,咱们看看完整的测试代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(@"ZEDDispatchGroupViewController viewDidLoad");
//第一步:建立group
NSLog(@"初始化group");
self.group = dispatch_group_create();
//第二步:追加任务到group
NSLog(@"使用dispatch_group_async方式追加任务1");
dispatch_group_async(self.group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操做
NSLog(@"1---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"任务1完成");
});
NSLog(@"使用dispatch_group_async方式追加任务2");
dispatch_group_async(self.group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操做
NSLog(@"2---%@",[NSThread currentThread]); // 打印当前线程
}
NSLog(@"任务2完成");
});
NSLog(@"使用dispatch_group_enter方式追加任务3");
//dispatch_group_enter与dispatch_group_leave必须成对出现
dispatch_group_enter(self.group);
//开启一个网络请求
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url =
[NSURL URLWithString:[@"https://www.baidu.com/" stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"GET";
NSLog(@"3---start---%@",[NSThread currentThread]);
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"%@", [error localizedDescription]);
}
if (data) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
NSLog(@"%@", dict);
}
NSLog(@"3---end---%@",[NSThread currentThread]);
NSLog(@"任务3完成");
dispatch_group_leave(self.group);
}];
[dataTask resume];
//第三步:添加group中任务所有完成的回调
NSLog(@"使用dispatch_group_notify添加异步任务所有完成的监听");
//dispatch_group_notify 的方式不会阻塞当前线程
dispatch_group_notify(self.group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"---全部任务所有执行完毕---");
});
//dispatch_group_wai会阻塞当前线程,直到group中的任务所有完成,才能继续往主队列中追加任务
// dispatch_group_wait(self.group, DISPATCH_TIME_FOREVER);
NSLog(@"---测试结束了---");
}
复制代码
测试结果log以下:
2019-04-25 17:07:21.432220+0800 GCD(三) dispatch_group[28759:5272759] libMobileGestalt MobileGestalt.c:890: MGIsDeviceOneOfType is not supported on this platform.
2019-04-25 17:07:21.543885+0800 GCD(三) dispatch_group[28759:5272759] ZEDDispatchGroupViewController viewDidLoad
2019-04-25 17:07:21.544044+0800 GCD(三) dispatch_group[28759:5272759] 初始化group
2019-04-25 17:07:21.544161+0800 GCD(三) dispatch_group[28759:5272759] 使用dispatch_group_async方式追加任务1
2019-04-25 17:07:21.544286+0800 GCD(三) dispatch_group[28759:5272759] 使用dispatch_group_async方式追加任务2
2019-04-25 17:07:21.544391+0800 GCD(三) dispatch_group[28759:5272759] 使用dispatch_group_enter方式追加任务3
2019-04-25 17:07:21.547318+0800 GCD(三) dispatch_group[28759:5272759] 3---start---<NSThread: 0x600002f86c00>{number = 1, name = main}
2019-04-25 17:07:21.548050+0800 GCD(三) dispatch_group[28759:5272759] 使用dispatch_group_notify添加异步任务所有完成的监听
2019-04-25 17:07:21.548173+0800 GCD(三) dispatch_group[28759:5272759] ---测试结束了---
2019-04-25 17:07:21.700314+0800 GCD(三) dispatch_group[28759:5272797] (null)
2019-04-25 17:07:21.700490+0800 GCD(三) dispatch_group[28759:5272797] 3---end---<NSThread: 0x600002fe0940>{number = 5, name = (null)}
2019-04-25 17:07:21.700611+0800 GCD(三) dispatch_group[28759:5272797] 任务3完成
2019-04-25 17:07:23.547004+0800 GCD(三) dispatch_group[28759:5272796] 1---<NSThread: 0x600002fde480>{number = 6, name = (null)}
2019-04-25 17:07:23.547076+0800 GCD(三) dispatch_group[28759:5272798] 2---<NSThread: 0x600002fde4c0>{number = 7, name = (null)}
2019-04-25 17:07:25.547612+0800 GCD(三) dispatch_group[28759:5272798] 2---<NSThread: 0x600002fde4c0>{number = 7, name = (null)}
2019-04-25 17:07:25.547634+0800 GCD(三) dispatch_group[28759:5272796] 1---<NSThread: 0x600002fde480>{number = 6, name = (null)}
2019-04-25 17:07:25.547901+0800 GCD(三) dispatch_group[28759:5272796] 任务1完成
2019-04-25 17:07:25.547910+0800 GCD(三) dispatch_group[28759:5272798] 任务2完成
2019-04-25 17:07:25.548138+0800 GCD(三) dispatch_group[28759:5272798] ---全部任务所有执行完毕---
复制代码
dispatch_group_async
与dispatch_group_enter
都是异步添加任务,不会阻塞当前线程
dispatch_group_notify
不会阻塞当前线程,dispatch_group_wait
会阻塞当前线程
dispatch_group_enter
与dispatch_group_leave
必须成对出现,不然group中的任务永远不会完成
若是文中有错误的地方,或者与你的想法相悖的地方,请在评论区告知我,我会继续改进,若是你以为这个篇文章总结的还不错,麻烦动动小手,给个人文章与Git代码样例
点个✨