GCD的基本使用

什么是GCD?

纯C语言,提供了非常多强大的函数

GCD的优势

GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核,四核)
GCD会自动管理线程的声明周期(创建线程,调度任务 ,销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

任务和队列

GCD中有2个核心概念
任务:执行什么操作
队列:用来存放任务

GCD的使用就2个步骤
1.定制任务:
确定想做的事情

2.将任务添加到队列中
GCD会自动将队列中的任务取出,放到对应的线程中执行
任务的取出遵循队列的FIFO原则,先进先出,后进后出

执行任务GCD中有2个用来执行任务的常用函数

用同步的方式执行任务

queue:队列
block:任务
dispatch_sync(dispatch_queue_t queue, <#^(void)block#>);

用异步的方式执行任务

dispatch_async(dispatch_queue_t queue, <#^(void)block#>);

队列的类型GCD的队列可以分为2大类型

并发队列:
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
并发功能只有在异步(dispatch_async)函数下才有效

串行队列:
让任务一个接着一个地执行(一个任务执行完毕后,在执行下一个任务)

容易混淆的术语

有4个术语比较容易混淆:同步、异步、并发、串行
同步和异步主要影响:能不能开启新的线程
同步:只是在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力

并发和串行主要影响:任务的执行方式
并发:允许多个任务并发同时执行
串行:一个任务执行完毕后,再执行下一个任务

创建队列

创建普通队列
第一个参数:C语言的字符串,标签
第二个参数:队列的类型
DISPATCH_QUEUE_SERIAL 串行队列
DISPATCH_QUEUE_CONCURRENT 并发队列

dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);

获得全局并发队列
第一个参数:优先级
第二个参数:此参数暂时无用,给未来使用,传0即可

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

使用Create函数创建的并发队列和全局并发队列的主要区别:

1.全局并发队列在整个应用程序中本身是默认存在的,并且对应有高优先级,默认优先级,低优先级和后台优先级一共四个并发队列,我们只是选择其中的一个直接拿来用。而Create函数是实打实的从头开始去创建一个队列
2.在iOS6.0之前,在GCD中凡是使用了带Create和retain的函数在最后都需要做一次release操作。而主队列和全局并发队列不需要我们手动release。当然了,在iOS6.0之后,GCD已经被纳入到了ARC的内存管理范畴中,即便是使用retain或者create函数创建的对象也不再需要开发人员手动释放,我们像对待普通OC对象一样对待GCD就可以了
3.在使用栅栏函数的时候,苹果官方明确规定栅栏函数只有在和使用create函数自己的创建的并发队列一起使用的时候才有效(没有给出具体原因)

四种使用方式

异步函数+并发队列
开启多条线程,队列中任务异步执行
并不是多少个任务就开多少个线程

- (void)asyncConcurrent
{
    // 1.创建队列
//    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
    
    // 获得全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
   
    // 2.1>封装任务 2.2>添加任务到队列中
    /**
     第一个参数:队列
     第二个参数:要执行的任务
     */
    dispatch_async(queue, ^{
        NSLog(@"download1--- %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"download2--- %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"download3-- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download4-- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download5-- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download6-- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download7-- %@",[NSThread currentThread]);
    });
}

异步函数+串行队列
会开启新线程,队列中任务是串行执行

- (void)asyncSerial
{
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{
        NSLog(@"download1--- %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"download2--- %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"download3-- %@",[NSThread currentThread]);
    });
}

同步函数+并发队列
不会开线程,任务是串行执行的


- (void)syncConcurrent
{
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);

    dispatch_sync(queue, ^{
        NSLog(@"download1--- %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"download2--- %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"download3-- %@",[NSThread currentThread]);
    });
}

同步函数+串行队列
不会开线程,任务是串行执行的


- (void)syncSerial
{
    dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_SERIAL);
 
    dispatch_sync(queue, ^{
        NSLog(@"download1--- %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"download2--- %@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"download3-- %@",[NSThread currentThread]);
    });
}

GCD实现线程间通信

 
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    
    // 1.创建子线程下载图片
    dispatch_async(dispatch_queue_create("com.520it.download", DISPATCH_QUEUE_CONCURRENT), ^{
        
        NSURL *url = [NSURL URLWithString:@"http://pic.sc.chinaz.com/files/pic/pic9/201508/apic14052.jpg"];

        NSData *imgData = [NSData dataWithContentsOfURL:url];
        
        UIImage *image = [UIImage imageWithData:imgData];
        
        // 子线程中处理
        dispatch_sync(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
        });
//        dispatch_async(dispatch_get_main_queue(), ^{
//            self.imageView.image = image;
//        });
    });
}

GCD队列组

#import "ViewController.h"
#import "AFNetworking.h"

@interface ViewController ()

@property (nonatomic, strong) UIImage *image1;

@property (nonatomic, strong) UIImage *image2;

@property (nonatomic, strong) UIImageView *imageView;

@end

@implementation ViewController

- (UIImageView *)imageView{
    if (!_imageView) {
        _imageView = [[UIImageView alloc]init];
        _imageView.frame = CGRectMake((self.view.frame.size.width - 200 )*0.5, (self.view.frame.size.height - 200 )*0.5, 200, 200);
        [self.view addSubview:_imageView];
    }
    return _imageView;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self queueGroup3];
}

- (void)queueGroup3
{
    /**
     需求:
     1.下载图片1,子线程
     2.下载图片2,开子线程
     3.合成并显示图片,开子线程
     */
    
    //    1.创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    //    2.创建队列组
    dispatch_group_t group = dispatch_group_create();
    
    //    3.异步函数
    //    1)封装任务
    //    2)把任务添加到队列中
    //    3)会监听任务的执行情况,通知group
    dispatch_group_async(group, queue, ^{
        NSLog(@"0-----%@",[NSThread currentThread]);
//        1.1确定url
        NSURL *URL = [NSURL URLWithString:@"http://pic.sc.chinaz.com/files/pic/pic9/201508/apic14052.jpg"];
//        1.2下载二进制数据
        NSData *imgData = [NSData dataWithContentsOfURL:URL];
        
//        1.3转换图片
        self.image1 = [UIImage imageWithData:imgData];
    });
    
    
    dispatch_group_async(group, queue, ^{
        //        2.1确定url
        NSURL *URL = [NSURL URLWithString:@"http://pic.sc.chinaz.com/files/pic/pic9/201508/apic14052.jpg"];
        //        2.2下载二进制数据
        NSData *imgData = [NSData dataWithContentsOfURL:URL];
        
        //        2.3转换图片
        self.image2 = [UIImage imageWithData:imgData];
    });
    //3.合并图片
    // 拦截通知,当队列组中所有的任务都执行完毕的时候会进入到下面的方法
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"notify-----%@",[NSThread currentThread]);
        //3.1 创建图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(200, 200));
        //3.2画图1
        [self.image1 drawInRect:CGRectMake(0, 0, 200, 100)];
        self.image1 = nil;
        //3.3画图2
        [self.image2 drawInRect:CGRectMake(0, 100, 200, 100)];
        self.image2 = nil;
        //3.4根据上下文得到一张图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        //3.5关闭上下文
        UIGraphicsEndImageContext();
        //3.6更新UI
//        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image;
//        });
    });
}

//跟group1功能相同
- (void)queueGroup2
{
    //    1.创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    //    2.创建队列组
    dispatch_group_t group = dispatch_group_create();
    //    3.再该方法后面的异步任务会被纳入到队列组的监听范围,进入群组
    dispatch_group_enter(group);
    
    dispatch_async(queue, ^{
        NSLog(@"1----%@",[NSThread currentThread]);
        //离开群组
        dispatch_group_leave(group);
    });
    //dispatch_group_enter(group);dispatch_group_leave(group);必须配对使用
    dispatch_group_enter(group);
    
    dispatch_async(queue, ^{
        NSLog(@"2----%@",[NSThread currentThread]);
        //离开群组
        dispatch_group_leave(group);
    });
    
    // 拦截通知,当队列组中所有的任务都执行完毕的时候会进入到下面的方法
    // 问题?该方法是阻塞的吗? 内部本身是异步的,不是堵塞的
    dispatch_group_notify(group, queue, ^{
        NSLog(@"notify-----%@",[NSThread currentThread]);
    });
    
    // 等待 .死等.直到队列组中所有的任务都执行完毕之后才能执行
    // 阻塞的
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"end");
}

- (void)queueGroup1
{
    //    1.创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    //    2.创建队列组
    dispatch_group_t group = dispatch_group_create();
    
    //    3.异步函数
    //    1)封装任务
    //    2)把任务添加到队列中
    //    3)会监听任务的执行情况,通知group
    dispatch_group_async(group, queue, ^{
        NSLog(@"0-----%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"1-----%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, queue, ^{
        NSLog(@"2-----%@",[NSThread currentThread]);
    });
    // 拦截通知,当队列组中所有的任务都执行完毕的时候会进入到下面的方法
    dispatch_group_notify(group, queue, ^{
        NSLog(@"notify-----%@",[NSThread currentThread]);
    });
}

@end

主队列的相关用法

使用主队列(跟主线程相关联的队列)
主队列是GCD自带的一种特殊的串行队列
放在主队列中的任务,都会放到主线程中执行
使用dispatch_get_main_queue()获得主队列

跟普通的串行队列不一样

异步函数+ 主队列:所有任务都在主线程中不会开线程,任务是串行执行

- (void)asyncMain{
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        NSLog(@"download4-- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download5-- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download6-- %@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"download7-- %@",[NSThread currentThread]);
    });
}

// 同步函数+ 主队列:
// 死锁原理:如果主队列发现当前主线程有任务在执行,那么主队列会暂停调用队列中的任务,直到主线程空闲为止
// 同步函数特点:如果同步函数没有执行完毕,那么后面的也别想执行
// 使用子线程调用即可解决

- (void)syncMain
{
    dispatch_queue_t queue = dispatch_get_main_queue();
    NSLog(@"start");
    dispatch_sync(queue, ^{
        NSLog(@"download4-- %@",[NSThread currentThread]);
    });
//    dispatch_sync(queue, ^{
//        NSLog(@"download5-- %@",[NSThread currentThread]);
//    });
 
    NSLog(@"end");
}

你可能感兴趣的:(GCD的基本使用)