我正在查看有关使用调度队列在一组任务完成时收到通知的Ray Wenderlich教程。 http://www.raywenderlich.com/63338/grand-central-dispatch-in-depth-part-2
“可行的代码”下显示的第一个代码直接来自教程。完成所有3次下载后,将执行警报视图(最终完成块)。
我尝试使用它并在“无法运行的代码”中将调度异步调低,以查看如果dispatch_group_create()和dispatch_group_enter()在不同的队列上发生会发生什么。在这种情况下,dispatch_group_enter()似乎没有注册,因为dispatch_group_wait()立即完成,并且即使在所有下载完成之前也会执行警报视图(最终完成块)。
有人可以解释第二种情况中发生的事情吗? (这只是为了我对dispatch组如何工作的理解,我意识到将整个函数放在全局并发队列中以避免阻塞主线程更好。)
有效的代码
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();
for (NSInteger i = 0; i < 3; i++)
{
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
dispatch_group_enter(downloadGroup);
__block Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
NSLog(@"Finished completion block for photo alloc for URL %@ and photo is %@",url,photo) ;
dispatch_group_leave(downloadGroup);
}];
[[PhotoManager sharedManager] addPhoto:photo];
NSLog(@"Finished adding photo to shared manager for URL %@ and photo is %@",url,photo) ;
}
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
NSLog(@"Executing completion block after download group complete") ;
completionBlock(error);
}
}) ;
}) ;
}
EDITED代码无法使用额外的NSLog语句
不起作用的代码
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();
for (NSInteger i = 0; i < 3; i++)
{
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
dispatch_group_enter(downloadGroup);
NSLog(@"Enetered group for URL %@",url) ;
__block Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
NSLog(@"Finished completion block for photo alloc for URL %@ and photo is %@",url,photo) ;
dispatch_group_leave(downloadGroup);
}];
[[PhotoManager sharedManager] addPhoto:photo];
NSLog(@"Finished adding photo to shared manager for URL %@ and photo is %@",url,photo) ;
}) ;
}
NSLog(@"Executing wait statement") ;
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
NSLog(@"Executing completion block after download group complete") ;
completionBlock(error);
}
}) ;
}
答案 0 :(得分:3)
“dispatch_group_enter()
似乎没有注册”,因为到dispatch_group_wait()
被调用时它实际上还没有被调用过。或者,相反,它不能保证被调用。有竞争条件。
这不是关于不同队列的具体内容。它是关于并发性和异步性的。
dispatch_async()
只是意味着“将任务添加到列表中”,其中隐含的理解是某些东西,某些地方,某些人将从该列表中取出任务并执行它们。在将任务放入列表后,它立即返回其调用者。它不会等待任务开始运行,更不用说完全运行了。
因此,您的for
循环运行得非常快,当它退出时,可能是已排队的任务的 none 已经启动。或者,如果有任何已经开始,可能是他们还没有完成进入该组。
您的代码可能会在任何内容进入群组之前完成对dispatch_group_wait()
的调用。
通常,您需要确保在调用dispatch_group_enter()
之前已完成对dispatch_group_wait()
的所有相关调用。最简单的方法是让它们在一个执行上下文中同步发生。也就是说,不要在异步调度的块内调用dispatch_group_enter()
。