Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【iOS】玩转-GCD #23

Open
ShowJoy-com opened this issue Mar 6, 2017 · 0 comments
Open

【iOS】玩转-GCD #23

ShowJoy-com opened this issue Mar 6, 2017 · 0 comments

Comments

@ShowJoy-com
Copy link
Owner

ShowJoy-com commented Mar 6, 2017

本文来自尚妆iOS团队嘉文
发表于尚妆github博客,欢迎订阅!

GCD介绍


Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法
基于C语言,提供了非常多强大的函数

术语


同步 (Synchronous)

在当前线程中执行任务,不具备开启新线程的能力
提交的任务在执行完成后才会返回
同步函数: dispatch_sync()

 

异步 (Asynchronous)

在新线程中执行任务,具备开启新线程的能力
提交的任务立刻返回,在后台队列中执行
异步函数: dispatch_async()

 

串行 (Serial)

一个任务执行完毕后,再执行下一个任务

 

并发 (Concurrent)

多个任务同时执行(自动开启多个线程),只有在异步函数下才有效

                  描述                                          说明               
                  queue                                   队列               
                  main                                 主队列               
                  global                                全局队列              
            dispatch_queue_t                           描述队列              
            dispatch_block_t                           描述任务              
            dispatch_once_t                            描述一次性              
            dispatch_time_t                            描述时间              
            dispatch_group_t                           描述队列组              
          dispatch_semaphore_t                         描述信号量              
                  函数                                          说明               
            dispatch_sync()                            同步执行              
            dispatch_async()                           异步执行              
            dispatch_after()                           延时执行              
            dispatch_once()                            一次性执行              
            dispatch_apply()                           提交队列              
        dispatch_queue_create()                      创建队列             
        dispatch_group_create()                        创建队列组              
          dispatch_group_async()                      提交任务到队列组            
dispatch_group_enter() / dispatch_group_leave() 将队列组中的任务未执行完毕的任务数目加减1(两个函数要配合使用)
        dispatch_group_notify()                      监听队列组执行完毕            
          dispatch_group_wait()                   设置等待时间(返回 0成功,1失败)       
注意:

1.所有的执行都放到队列中(queue),队列的特点是FIFO(先提交的先执行)
2.必须在主线程访问 UIKit 的类
3.并发队列只在异步函数下才有效

#基本使用

 NSLog(@"当前线程: %@", [NSThread currentThread]);
 //获取主队列
 dispatch_queue_t mainQueue = dispatch_get_main_queue();
 
 //获取全局并发队列
 dispatch_queue_t otherQueue =   dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
 //同步函数(在当前线程中执行,不具备开启新线程的能力)
 dispatch_sync(otherQueue, ^{
      NSLog(@"同步 %@", [NSThread currentThread]);
 });
    
 //异步函数(在另一条线程中执行,具备开启新线程的能力)
 dispatch_async(otherQueue, ^{
      NSLog(@"异步 %@", [NSThread currentThread]);
 });
    
 //输出:   当前线程: <NSThread: 0x7f81bac06670>{number = 1, name = main}
 //输出:   同步 <NSThread: 0x7f81bac06670>{number = 1, name = main}
 //输出:   异步 <NSThread: 0x7f81bae35be0>{number = 3, name = (null)}
延时执行 dispatch_after()

dispatch_after()延迟一段时间把一项任务提交到队列中执行,返回之后就不能取消
常用来在在主队列上延迟执行一项任务

    NSLog(@"当前线程 %@", [NSThread currentThread]);

    //GCD延时调用(主线程)(主队列)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"GCD延时(主线程) %@", [NSThread currentThread]);
    });
    
    //GCD延时调用(其他线程)(全局并发队列)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"GCD延时(其他线程) %@", [NSThread currentThread]);
    });

    //输出:   当前线程 <NSThread: 0x7f8cb9701990>{number = 1, name = main}
    //输出:   GCD延时(主线程) <NSThread: 0x7f8cb9701990>{number = 1, name = main}
    //输出:   GCD延时(其他线程) <NSThread: 0x7f8cb9513ee0>{number = 3, name = (null)}
一次性执行 dispatch_once()

整个程序运行中,只会执行一次 (默认线程是安全的)
dispatch_once() 以线程安全的方式执行且仅执行其代码块一次

    for (NSInteger i = 0; i < 10; i++) {
      
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"GCD一次性执行(默认线程是安全的)");
        });
        
    }

 //输出:   GCD一次性执行(默认线程是安全的)
//使用GCD初始化单例
+ (instancetype)sharedManager { 
  
    static PhotoManager *sharedPhotoManager = nil; 
    static dispatch_once_t onceToken; 
    dispatch_once(&onceToken, ^{ 
        sharedPhotoManager = [[PhotoManager alloc] init]; 
    }); 

    return sharedPhotoManager; 
} 
提交 dispatch_apply()

把一项任务提交到队列中多次执行,具体是并行执行还是串行执行由队列本身决定
dispatch_apply不会立刻返回,在执行完毕后才会返回,是同步的调用。

队列

任务1,任务2依次执行,所有任务都执行成功后回到主线程
(效率不高)

 NSLog(@"当前线程 %@", [NSThread currentThread]);
   
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        //全局并发队列
        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任务1 %@", [NSThread currentThread]);
        }
        
        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任务2 %@", [NSThread currentThread]);
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            //主队列
            NSLog(@"主线程执行(刷新UI) %@", [NSThread currentThread]);
        });
        
    });
    
    //输出:   当前线程 <NSThread: 0x7fa78040b8b0>{number = 1, name = main}
    //输出:   任务1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务1 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7f9da1416a00>{number = 3, name = (null)}
    //输出:   主线程(刷新UI) <NSThread: 0x7f9da1705940>{number = 1, name = main}
队列组

任务1,任务2同时执行,所有任务都执行成功后回到主线程
(效率高)

    NSLog(@"当前线程 %@", [NSThread currentThread]);
    
    //(1)创建一个队列组
    dispatch_group_t group= dispatch_group_create();
    
    //(2)开启任务1
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任务1 %@", [NSThread currentThread]);
        }
        
    });
    
    //(3)开启任务2
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
       
        for (NSInteger i = 0; i < 5; i++) {
            NSLog(@"任务2 %@", [NSThread currentThread]);
        }
        
    });
    
    //(4)所有任务执行完毕,回到主线程
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        
        NSLog(@"主线程(刷新UI) %@", [NSThread currentThread]);
        
    });
    
    //输出:   当前线程 <NSThread: 0x7fd951704e70>{number = 1, name = main}
    //输出:   任务1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //输出:   任务1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //输出:   任务1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //输出:   任务2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //输出:   任务1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //输出:   任务1 <NSThread: 0x7ff65a50b4d0>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //输出:   任务2 <NSThread: 0x7ff65a697770>{number = 4, name = (null)}
    //输出:   主线程(刷新UI) <NSThread: 0x7ff65a406fb0>{number = 1, name = main}

#串行与并发

各个队列的执行效果

串行队列

串行队列

一个任务执行完毕后,再执行下一个任务
主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行

    //(1)使用dispatch_queue_create函数创建串行队列
    //参数1: 队列名称
 //参数2: 队列属性 (一般用NULL)
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
    
    //(2)使用主队列(跟主线程相关联的队列)
    dispatch_queue_t serialMainQueue = dispatch_get_main_queue();
并发队列

并发队列

多个任务并发执行(自动开启多个线程同时执行任务)

并发功能只有在异步(dispatch_async)函数下才有效!!!

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

              并发队列优先级                   快捷值      优先级 
    DISPATCH_QUEUE_PRIORITY_HIGH        2       高  
  DISPATCH_QUEUE_PRIORITY_DEFAULT       0     中(默认)
    DISPATCH_QUEUE_PRIORITY_LOW       (-2)      低  
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN   后台  
 //(1)使用dispatch_get_global_queue函数获得全局的并发队列
    //参数1: 优先级
 //参数2: 暂时无用参数 (传0)
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
异步函数_并发队列

(开启新线程,并发执行任务)

    NSLog(@"当前线程 %@", [NSThread currentThread]);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务1 %@", [NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务2 %@", [NSThread currentThread]);
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任务3 %@", [NSThread currentThread]);
    });
    
    //输出:   当前线程 <NSThread: 0x7fbe83c067c0>{number = 1, name = main}
    //输出:   任务1 <NSThread: 0x7fb84160ed90>{number = 3, name = (null)}
    //输出:   任务3 <NSThread: 0x7fb841752940>{number = 4, name = (null)}
    //输出:   任务2 <NSThread: 0x7fb8414167b0>{number = 5, name = (null)}
异步函数_串行队列

(开启新线程,串行执行任务)

    NSLog(@"当前线程 %@", [NSThread currentThread]);
    //创建串行队列
    dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
    
    dispatch_async(serialQueue, ^{
        NSLog(@"任务1 %@", [NSThread currentThread]);
    });
    
    dispatch_async(serialQueue, ^{
        NSLog(@"任务2 %@", [NSThread currentThread]);
    });
    
    dispatch_async(serialQueue, ^{
        NSLog(@"任务3 %@", [NSThread currentThread]);
    });
    
    //输出:   当前线程 <NSThread: 0x7fc192e05380>{number = 1, name = main}
    //输出:   任务1 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
    //输出:   任务2 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
    //输出:   任务3 <NSThread: 0x7fc192c16670>{number = 3, name = (null)}
同步函数_并发队列

(不会开启新线程,并发执行任务失效!)

    NSLog(@"当前线程 %@", [NSThread currentThread]);
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务1 %@", [NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务2 %@", [NSThread currentThread]);
    });
    
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务3 %@", [NSThread currentThread]);
    });
    
    //输出:   当前线程 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
    //输出:   任务1 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
    //输出:   任务2 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
    //输出:   任务3 <NSThread: 0x7fa0dbf02e20>{number = 1, name = main}
同步函数_串行队列

(不会开启新线程,串行执行任务)

 NSLog(@"当前线程 %@", [NSThread currentThread]);
 //创建串行队列
 dispatch_queue_t serialQueue = dispatch_queue_create("serialQueue", NULL);
 dispatch_sync(serialQueue, ^{
     NSLog(@"任务1 %@", [NSThread currentThread]);
 });

 dispatch_sync(serialQueue, ^{
      NSLog(@"任务2 %@", [NSThread currentThread]);
 });

 dispatch_sync(serialQueue, ^{
     NSLog(@"任务3 %@", [NSThread currentThread]);
 });

 //输出:   当前线程 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
 //输出:   任务1 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
 //输出:   任务2 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
 //输出:   任务3 <NSThread: 0x7f8c0b407f00>{number = 1, name = main}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant