dispatch_group_async、dispatch_group_notify

特点:当任务管理中的任务执行完会通知函数 dispatch_group_notify

我们经常遇到这样的面试题:异步下载几张图片、等待所有图片下载完成、合并一张大图、更新UI等等之类的需求。今天我们就用队列组解决这个问题。这里要用到dispatch_group_notify函数。

效果如下图所示:(为了便于理解、三张异步下载的小图也加载出来)



代码部分如下

//三张小图数组
    NSMutableArray *array1;
    //一张大图数组
    NSMutableArray *array2;
    
    //图片一二进制数据
    NSData *data1;
    //图片二二进制数据
    NSData *data2;
    //图片三二进制数据
    NSData *data3;
    
- (void)groupNotify
{
    NSLog(@"当前的线程为:%@",[NSThread currentThread]);
    NSLog(@"开始");
    dispatch_group_t group = dispatch_group_create();
    
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i < 1; ++i) {
            [NSThread sleepForTimeInterval:1];
            
            NSLog(@"我就是任务一、来自线程:%@",[NSThread currentThread]);
            
            NSURL *url = [NSURL URLWithString:@"http://imgsrc.baidu.com/imgad/pic/item/34fae6cd7b899e51fab3e9c048a7d933c8950d21.jpg"];
            self->data1 = [NSData dataWithContentsOfURL:url];
            UIImageView *imageView = self->array1[0];
            dispatch_async(dispatch_get_main_queue(), ^{
                
                imageView.image = [UIImage imageWithData:self->data1];
                
            });
            
        }
    });
 
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i < 1; ++i) {
            [NSThread sleepForTimeInterval:1];
            
            NSLog(@"我就是任务二、来自线程:%@",[NSThread currentThread]);
            
            NSURL *url = [NSURL URLWithString:@"http://img17.3lian.com/d/file/201702/18/2b5f1b6298411b0045c5562da02fc6ac.jpg"];
            self->data2 = [NSData dataWithContentsOfURL:url];
            UIImageView *imageView = self->array1[1];
            dispatch_async(dispatch_get_main_queue(), ^{
                
                imageView.image = [UIImage imageWithData:self->data2];
            
            });
            
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务3
        for (int i = 0; i < 1; ++i) {
            [NSThread sleepForTimeInterval:1];
            
            NSLog(@"我就是任务三、来自线程:%@",[NSThread currentThread]);
            
            NSURL *url = [NSURL URLWithString:@"http://img17.3lian.com/d/file/201702/23/dc458f76475470db9a5791848cb67801.jpg"];
            self->data3 = [NSData dataWithContentsOfURL:url];
            UIImageView *imageView = self->array1[2];
            dispatch_async(dispatch_get_main_queue(), ^{
               
                imageView.image = [UIImage imageWithData:self->data3];
            
            });
        }
        
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 所有图片下载完毕、合成一张图
        for (int i = 0; i < 1; ++i) {
            [NSThread sleepForTimeInterval:1];
            
            UIImageView *imageView1 = self->array2[0];
            UIImageView *imageView2 = self->array2[1];
            UIImageView *imageView3 = self->array2[2];
            imageView1.image = [UIImage imageWithData:self->data1];
            imageView2.image = [UIImage imageWithData:self->data2];
            imageView3.image = [UIImage imageWithData:self->data3];
            NSLog(@"我是要等任务-、任务二、任务三完成以后的最终任务、来自线程:%@",[NSThread currentThread]);
        }
        
        NSLog(@"结束");
    });
}
复制代码

打印结果:

2019-01-24 15:54:22.755688+0800 多线程demo[8498:280372] 当前的线程为:<NSThread: 0x60400006b9c0>{number = 1, name = main}
2019-01-24 15:54:22.755843+0800 多线程demo[8498:280372] 开始
2019-01-24 15:54:23.760460+0800 多线程demo[8498:280446] 我就是任务三、来自线程:<NSThread: 0x604000477640>{number = 5, name = (null)}
2019-01-24 15:54:23.760459+0800 多线程demo[8498:280447] 我就是任务二、来自线程:<NSThread: 0x604000477680>{number = 4, name = (null)}
2019-01-24 15:54:23.760459+0800 多线程demo[8498:280444] 我就是任务一、来自线程:<NSThread: 0x600000264b80>{number = 3, name = (null)}
2019-01-24 15:54:31.972052+0800 多线程demo[8498:280372] 我是要等任务-、任务二、任务三完成以后的最终任务、来自线程:<NSThread: 0x60400006b9c0>{number = 1, name = main}
2019-01-24 15:54:31.972156+0800 多线程demo[8498:280372] 结束
复制代码

分析:任务一、任务二、任务三采用异步下载(有各自的线程)、所以下载过程中是交错的而不是顺序执行,这也使得UI流畅,体验感好。所有任务完成时候、会走进dispatch_group_notify函数、在主线程更新UI,从而完成需求。

dispatch_group_enter、dispatch_group_leave

同样是类似上面的需求、当不用dispatch_group_async函数时、结合以上三个函数也能实现。

dispatch_group_enter、dispatch_group_leave成对出现、前者任务+1后者任务—1。当为0时才会追加到dispatch_group_notify函数中。

代码如下:(第一种注释dispatch_group_enter、dispatch_group_leave第二种打开注释)

NSLog(@"当前线程为:%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"开始");
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
             sleep(1);
            NSLog(@"我是任务一、来自线程%@",[NSThread currentThread]);
        }
//        dispatch_group_leave(group);
    });
    
//    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            sleep(1);
            NSLog(@"我是任务二、来自线程%@",[NSThread currentThread]);
        }
//        dispatch_group_leave(group);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        
        for (int i = 0; i < 2; ++i) {
             sleep(1);
            NSLog(@"我是任务三、来自线程%@",[NSThread currentThread]);
        }
        NSLog(@"结束");
    });
复制代码

打印结果一:(注释dispatch_group_enter、dispatch_group_leave)

2019-01-24 16:55:42.769970+0800 多线程demo[9519:323831] 当前线程为:<NSThread: 0x604000071100>{number = 1, name = main}
2019-01-24 16:55:42.770108+0800 多线程demo[9519:323831] 开始
2019-01-24 16:55:43.773884+0800 多线程demo[9519:323889] 我是任务一、来自线程<NSThread: 0x60400047a000>{number = 3, name = (null)}
2019-01-24 16:55:43.773902+0800 多线程demo[9519:323890] 我是任务二、来自线程<NSThread: 0x600000276d40>{number = 4, name = (null)}
2019-01-24 16:55:43.800804+0800 多线程demo[9519:323831] 我是任务三、来自线程<NSThread: 0x604000071100>{number = 1, name = main}
2019-01-24 16:55:44.776018+0800 多线程demo[9519:323889] 我是任务一、来自线程<NSThread: 0x60400047a000>{number = 3, name = (null)}
2019-01-24 16:55:44.776010+0800 多线程demo[9519:323890] 我是任务二、来自线程<NSThread: 0x600000276d40>{number = 4, name = (null)}
2019-01-24 16:55:44.802165+0800 多线程demo[9519:323831] 我是任务三、来自线程<NSThread: 0x604000071100>{number = 1, name = main}
2019-01-24 16:55:44.802362+0800 多线程demo[9519:323831] 结束
复制代码

分析结果:没有dispatch_group_enter、dispatch_group_leave函数、三个任务交错执行、互不影响。

打印结果二:(打开注释)

2019-01-24 17:00:05.609413+0800 多线程demo[9608:327439] 当前线程为:<NSThread: 0x604000068ac0>{number = 1, name = main}
2019-01-24 17:00:05.609562+0800 多线程demo[9608:327439] 开始
2019-01-24 17:00:06.614555+0800 多线程demo[9608:327506] 我是任务二、来自线程<NSThread: 0x60400046de00>{number = 3, name = (null)}
2019-01-24 17:00:06.614622+0800 多线程demo[9608:327507] 我是任务一、来自线程<NSThread: 0x60400027f4c0>{number = 4, name = (null)}
2019-01-24 17:00:07.620524+0800 多线程demo[9608:327506] 我是任务二、来自线程<NSThread: 0x60400046de00>{number = 3, name = (null)}
2019-01-24 17:00:07.620543+0800 多线程demo[9608:327507] 我是任务一、来自线程<NSThread: 0x60400027f4c0>{number = 4, name = (null)}
2019-01-24 17:00:08.620973+0800 多线程demo[9608:327439] 我是任务三、来自线程<NSThread: 0x604000068ac0>{number = 1, name = main}
2019-01-24 17:00:09.621951+0800 多线程demo[9608:327439] 我是任务三、来自线程<NSThread: 0x604000068ac0>{number = 1, name = main}
2019-01-24 17:00:09.622129+0800 多线程demo[9608:327439] 结束
复制代码

分析结果:任务三始终在任务一、二结束完毕执行。任务一、任务二交错执行。

dispatch_group_wait

特点:group中的任务完成之后 才能执行下面的函数。 代码如下:

NSLog(@"当前线程为:%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"开始");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 任务1
        for (int i = 0; i < 2; i ++) {
            sleep(1);
            NSLog(@"我是任务一、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 任务2
        for (int i = 0; i < 2; i ++) {
            sleep(1);
            NSLog(@"我是任务二、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 任务3
        for (int i = 0; i < 2; i ++) {
            sleep(1);
            NSLog(@"我是任务三、来自线程:%@",[NSThread currentThread]);
        }
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"我一定要最后离开、因为我爱你");
复制代码

打印结果

1.没dispatch_group_wait函数

2019-01-24 17:08:22.912309+0800 多线程demo[9777:334058] 当前线程为:<NSThread: 0x600000261500>{number = 1, name = main}
2019-01-24 17:08:22.912460+0800 多线程demo[9777:334058] 开始
2019-01-24 17:08:22.912600+0800 多线程demo[9777:334058] 我一定要最后离开、因为我爱你
2019-01-24 17:08:23.918052+0800 多线程demo[9777:334133] 我是任务三、来自线程:<NSThread: 0x6000004697c0>{number = 4, name = (null)}
2019-01-24 17:08:23.918048+0800 多线程demo[9777:334131] 我是任务一、来自线程:<NSThread: 0x60000046efc0>{number = 3, name = (null)}
2019-01-24 17:08:23.918050+0800 多线程demo[9777:334132] 我是任务二、来自线程:<NSThread: 0x604000660800>{number = 5, name = (null)}
2019-01-24 17:08:24.923557+0800 多线程demo[9777:334132] 我是任务二、来自线程:<NSThread: 0x604000660800>{number = 5, name = (null)}
2019-01-24 17:08:24.923557+0800 多线程demo[9777:334133] 我是任务三、来自线程:<NSThread: 0x6000004697c0>{number = 4, name = (null)}
2019-01-24 17:08:24.923557+0800 多线程demo[9777:334131] 我是任务一、来自线程:<NSThread: 0x60000046efc0>{number = 3, name = (null)}

复制代码

分析: NSLog(@"我一定要最后离开、因为我爱你");并没有最后离开。所有任务交错执行。

2.有dispatch_group_wait函数

2019-01-24 17:14:23.603205+0800 多线程demo[9928:339432] 当前线程为:<NSThread: 0x60400006c280>{number = 1, name = main}
2019-01-24 17:14:23.603338+0800 多线程demo[9928:339432] 开始
2019-01-24 17:14:24.605474+0800 多线程demo[9928:339503] 我是任务三、来自线程:<NSThread: 0x600000467bc0>{number = 3, name = (null)}
2019-01-24 17:14:24.605474+0800 多线程demo[9928:339495] 我是任务二、来自线程:<NSThread: 0x6040002781c0>{number = 4, name = (null)}
2019-01-24 17:14:24.605474+0800 多线程demo[9928:339493] 我是任务一、来自线程:<NSThread: 0x600000467b00>{number = 5, name = (null)}
2019-01-24 17:14:25.606206+0800 多线程demo[9928:339495] 我是任务二、来自线程:<NSThread: 0x6040002781c0>{number = 4, name = (null)}
2019-01-24 17:14:25.606220+0800 多线程demo[9928:339503] 我是任务三、来自线程:<NSThread: 0x600000467bc0>{number = 3, name = (null)}
2019-01-24 17:14:25.606261+0800 多线程demo[9928:339493] 我是任务一、来自线程:<NSThread: 0x600000467b00>{number = 5, name = (null)}
2019-01-24 17:14:25.606992+0800 多线程demo[9928:339432] 我一定要最后离开、因为我爱你
复制代码

分析:确实最后离开了、dispatch_group_wait阻塞了当前线程、dispatch_group_wait后面的方法不能执行。