zoukankan      html  css  js  c++  java
  • iOS多线程--GCD

    简单介绍下GCD:

    GCD:Grand Central Dispatch(伟大的中枢调度器),纯C的函数,提供了很多非常强大的功能。

    它是苹果公司为多核的并行运算提出的解决方案,具有以下优点

    1.GCD会自动利用更多的CPU内核(双核、四核等)

    2.自动管理线程的生命周期(创建任务、调度任务、销毁线程)

    3.程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码,所以它大大解放了程序员

    在GCD中有两个核心的概念:任务和队列。任务就是你要执行什么操作 队列就是用来存放任务

    使用GCD一般有两个步骤:

    1.定制任务,确定你要进行的操作

    2.将任务添加到队列中,GCD会自动将任务从队列中取出(FIFO,先进先出原则),放到相应的线程中执行

    GCD中有两个主要执行任务的函数:

    1.同步:(不能开启新线程)

    dispatch_sync(dispatch_queue_t queue, dispatch_block_t block)

    2.异步:(开启新线程)

    dispatch_async(dispatch_queue_t queue, dispatch_block_t block)

    GCD中的队列有可以分为两大类型:

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

    2.串行队列:任务一个接一个有序执行

    GCD默认已经提供了全局的并发队列,供整个应用使用,不需要手动创建:使用dispatch_get_global_queue函数获得全局的并发队列

    串行队列有两种方式获得:1.dispatch_queue_create创建 2.使用主队列dispatch_get_main_queue(),主队列是GCD自带的一种特殊的串行队列,放在主队列的任务都会在主线程中执行

    上代码验证下:

    1.异步函数在并发队列中执行

    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // 1.获得全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
        // 2.添加任务到队列中执行任务
        dispatch_async(queue, ^{
            NSLog(@"下载1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"下载2----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"下载3----%@",[NSThread currentThread]);
        });
    
    };

    结果如图:

    可见程序在多线程中并发执行

    2.异步函数在串行队列中执行:

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self asyncSerialQueue];
    }
    
    #pragma mark 异步+串行
    -(void)asyncSerialQueue
    {
        // 1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("gcd.queue", NULL);
        // 2.添加任务到队列中执行任务
        dispatch_async(queue, ^{
            NSLog(@"下载1----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"下载2----%@",[NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"下载3----%@",[NSThread currentThread]);
        });
    }

    结果如图:

    可见程序开启单个线程按顺序执行

    3.在同步函数中执行并发任务

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self syncConcurrentQueue];
    }
    
    #pragma mark 同步+并发
    -(void)syncConcurrentQueue
    {
        // 1.获得全局的并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        // 2.添加任务到队列中执行任务
        dispatch_sync(queue, ^{
            NSLog(@"下载1----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"下载2----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"下载3----%@",[NSThread currentThread]);
        });
    }

    结果如图:

    在主线程中按顺序执行,并发队列失去并发功能

    4.同步执行串行队列:

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self syncSerialQueue];
    }
    #pragma mark 同步+串行
    -(void)syncSerialQueue
    {
        // 1.创建串行队列
        dispatch_queue_t queue = dispatch_queue_create("gcd.queue", NULL);
        // 2.添加任务到队列中执行任务
        dispatch_sync(queue, ^{
            NSLog(@"下载1----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"下载2----%@",[NSThread currentThread]);
        });
        dispatch_sync(queue, ^{
            NSLog(@"下载3----%@",[NSThread currentThread]);
        });
    }

    结果同第三种情况,不会开启新的线程,按顺序执行

    这里要特别注意,如果使用同步函数在主队列中执行,会造成线程死锁的情况

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1");
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"2");
        });
        NSLog(@"3");
    }

    输出结果为1

    那么使用异步函数在主队列中执行呢?

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"1----%@",[NSThread currentThread]);
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"2----%@",[NSThread currentThread]);
        });
        NSLog(@"3----%@",[NSThread currentThread]);
    }

    结果为:

    结果很特殊:不会开启新的线程,而是主队列中的任务会缓一缓,最后执行

    综上来看,各种队列执行情况如下表:

    关于GCD还有其他用法

    1.延时执行:

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        // 通常的延时执行
        dispatch_async(dispatch_get_main_queue(), ^{
            [self performSelector:@selector(run) withObject:nil afterDelay:1.0];
        });
        
        // gcd函数执行
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
        dispatch_after(when, queue ^{
            [self run];
        });
        
    }

    2.保证某段代码在执行过程中只执行一次(可用于实现单例模式):

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            // 执行一次的操作代码
        });
        
    }

    3.队列组(通常用于耗时操作)

    例如下载两张图片,要等两张图片都下载完成后,合并两张图,再更新主界面。由于下载图片时间长短不可控,使用普通方法会很困难,这时使用队列组可高效解决

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        // 创建一个组
        dispatch_group_t group = dispatch_group_create();
        // 下载图片1
        __block UIImage *image1 = nil;
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            image1 = [self downloadImageWithUrl:@"https://www.baidu.com/img/bdlogo.png"];
        });
        // 下载图片2
        __block UIImage *image2 = nil;
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            image2 = [self downloadImageWithUrl:@"http://img1.bdstatic.com/static/home/widget/search_box_home/logo/home_white_logo_0ddf152.png"];
        });
        // group任务都执行完毕,再执行其他操作
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            // 合并图片
            UIGraphicsBeginImageContext(CGSizeMake(300, 300));
            [image1 drawInRect:CGRectMake(0, 0, 150, 300)];
            [image2 drawInRect:CGRectMake(150, 0, 150, 300)];
            UIGraphicsEndImageContext();
        });
        
    }
    -(UIImage *)downloadImageWithUrl:(NSString *)urlStr
    {
    
        NSURL *url = [NSURL URLWithString:urlStr];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        return image;
    }

    4.定时器

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
    
        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
        dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 15ull*NSEC_PER_SEC, 1ull*NSEC_PER_SEC);
        dispatch_source_set_event_handler(timer, ^{
            NSLog(@"wake up");
            dispatch_source_cancel(timer);
        });
        dispatch_source_set_cancel_handler(timer, ^{
            NSLog(@"cancel");
        });
        // 启动
        dispatch_resume(timer);
        
    }

    此外,gcd还可监听文件的读和写,信号的中断等,有兴趣的可以进一步钻研

    示例代码可从Github下载https://github.com/VampireTMAC/GCDdemo

  • 相关阅读:
    马云演讲:给自己一个梦想,给自己一个承诺,给自己一个坚持!
    转:如何成为一个伟大的开发者
    数据挖掘之七种常用的方法
    windows命令行
    100万亿意味着什么?
    ubuntu环境配置
    Ubuntu runlevel修改
    Ubuntu 用户及组管理
    Git学习笔记
    Git详解之三 Git分支
  • 原文地址:https://www.cnblogs.com/tmacforever/p/4717520.html
Copyright © 2011-2022 走看看