GCD 总结
通过前面两篇文章和联系,应该学到了多线程和GCD的知识。
但是没有不断的使用和练习,我们永远也不能灵活使用。
这一篇文章就是对GCD的总结,概念是不会出现在文章中。
文章只是简单总结了GCD的一些方法的运用和什么时候运用。
dispatch_async
dispatch_async 是最简单也是最基础的GCD使用
它将开启新的线程来运行block中的代码,并且返回我们需要的值。
例:
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
- UIImage *overlayImage = [self faceOverlayImageFromImage:_image];
- dispatch_async(dispatch_get_main_queue(), ^{
- [self fadeInNewImage:overlayImage];
- });
- });
使用时间:
1. 自定义串行队列:当你想串行执行后台任务并追踪它时就是一个好选择。这消除了资源争用,因为你知道一次只有一个任务在执行。注意若你需要来自某个方法的数据,你必须内联另一个 Block 来找回它或考虑使用 dispatch_sync。
2. 主队列(串行):这是在一个并发队列上完成任务后更新 UI 的共同选择。要这样做,你将在一个 Block 内部编写另一个 Block 。以及,如果你在主队列调用 dispatch_async 到主队列,你能确保这个新任务将在当前方法完成后的某个时间执行。
3. 并发队列:这是在后台执行非 UI 工作的共同选择。
dispatch_after
dispatch_after 用来延后我们将要进行的操作
例:
- double delayInSeconds = 1.0;
- dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
- dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
- if (!count) {
- [self.navigationItem setPrompt:@"Add photos with faces to Googlyify them!"];
- } else {
- [self.navigationItem setPrompt:nil];
- }
- });
使用时间:
1. 自定义串行队列:在一个自定义串行队列上使用 dispatch_after 要小心。你最好坚持使用主队列。
2. 主队列(串行):是使用 dispatch_after 的好选择;Xcode 提供了一个不错的自动完成模版。
3. 并发队列:在并发队列上使用 dispatch_after 也要小心;你会这样做就比较罕见。还是在主队列做这些操作吧。
dispatch_once()
dispatch_once() 永远只会运行一次的线程,用来保护线程安全
例:
- + (instancetype)sharedManager
- {
- static PhotoManager *sharedPhotoManager = nil;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- sharedPhotoManager = [[PhotoManager alloc] init];
- sharedPhotoManager->_photosArray = [NSMutableArray array];
- });
- return sharedPhotoManager;
- }
那,上述例子就是保证单例类在项目中只会实例化一次。
使用须知:
这只是让访问共享实例线程安全。它绝对没有让类本身线程安全。类中可能还有其它竞态条件,例如任何操纵内部数据的情况。这些需要用其它方式来保证线程安全
Dispatch barriers
Dispatch barriers 是一组函数,在并发队列上工作时扮演一个串行式的瓶颈。使用 GCD 的障碍(barrier)API 确保提交的 Block 在那个特定时间上是指定队列上唯一被执行的条目。这就意味着所有的先于调度障碍提交到队列的条目必能在这个 Block 执行前完成。
例:
- dispatch_barrier_async(self.concurrentPhotoQueue, ^{
- [_photosArray addObject:photo];
- dispatch_async(dispatch_get_main_queue(), ^{
- [self postContentAddedNotification];
- });
- });
dispatch_sync()
dispatch_sync()同步地提交工作并在返回前等待它完成。使用 dispatch_sync 跟踪你的调度障碍工作,或者当你需要等待操作完成后才能使用 Block 处理过的数据。如果你使用第二种情况做事,你将不时看到一个 __block 变量写在 dispatch_sync 范围之外,以便返回时在 dispatch_sync 使用处理过的对象。
例:
- __block NSArray *array;
- dispatch_sync(self.concurrentPhotoQueue, ^{
- array = [NSArray arrayWithArray:_photosArray];
- });
注意:你需要很小心。想像如果你调用 dispatch_sync 并放在你已运行着的当前队列。这会导致死锁,因为调用会一直等待直到 Block 完成,但 Block 不能完成(它甚至不会开始!),直到当前已经存在的任务完成,而当前任务无法完成!这将迫使你自觉于你正从哪个队列调用——以及你正在传递进入哪个队列。
何时使用:
1. 自定义串行队列:在这个状况下要非常小心!如果你正运行在一个队列并调用 dispatch_sync 放在同一个队列,那你就百分百地创建了一个死锁。
2. 主队列(串行):同上面的理由一样,必须非常小心!这个状况同样有潜在的导致死锁的情况。
3. 并发队列:这才是做同步工作的好选择,不论是通过调度障碍,或者需要等待一个任务完成才能执行进一步处理的情况。
dispatch_sync() 和 dispatch_async() 的区别
dispatch_sync 添加任务到一个队列并等待直到任务完成。dispatch_async 做类似的事情,但不同之处是它不会等待任务的完成,而是立即继续“调用线程”的其它任务。
dispatch_group
dispatch_group Dispatch Group 会在整个组的任务都完成时通知你。这些任务可以是同步的,也可以是异步的,即便在不同的队列也行。而且在整个组的任务都完成时,Dispatch Group 可以用同步的或者异步的方式通知你。因为要监控的任务在不同队列,那就用一个 dispatch_group_t 的实例来记下这些不同的任务。
例:
- - (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_group_enter(downloadGroup);
- Photo *photo = [[Photo alloc] initwithURL:url
- withCompletionBlock:^(UIImage *image, NSError *_error) {
- if (_error) {
- error = _error;
- }
- dispatch_group_leave(downloadGroup);
- }];
-
- [[PhotoManager sharedManager] addPhoto:photo];
- }
-
- dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
- if (completionBlock) {
- completionBlock(error);
- }
- });
- }
何时使用:
1. 自定义串行队列:它很适合当一组任务完成时发出通知。
2. 主队列(串行):它也很适合这样的情况。但如果你要同步地等待所有工作地完成,那你就不应该使用它,因为你不能阻塞主线程。然而,异步模型是一个很有吸引力的能用于在几个较长任务(例如网络调用)完成后更新 UI 的方式。
3. 并发队列:它也很适合 Dispatch Group 和完成时通知。
dispatch_apply
dispatch_apply 表现得就像一个 for 循环,但它能并发地执行不同的迭代。这个函数是同步的,所以和普通的 for 循环一样,它只会在所有工作都完成后才会返回。
例:
- dispatch_apply(3, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t 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;
- }
-
何时使用:
1. 自定义串行队列:串行队列会完全抵消 dispatch_apply 的功能;你还不如直接使用普通的 for 循环。
2. 主队列(串行):与上面一样,在串行队列上不适合使用 dispatch_apply 。还是用普通的 for 循环吧。
3. 并发队列:对于并发循环来说是很好选择,特别是当你需要追踪任务的进度时。
以上就是对前面三篇文章的一个总结,我将提到的方法都取出来,解释,举例,并简单告知大家该在什么时候使用。
如果你还是不太明白怎么使用,那你还是需要仔细看看前面的文章,然后多联系,多看其他的文章。