GCD串行,GCD并行,GCD同步与异步,

1. GCD 简介

  什么是 GCD 呢???

  哈哈,啰嗦一下,想直接看代码的可以直接看第4条

  答曰:Grand Central Dispatch(GCD) 是 Apple 开发的一个多核编程的较新的解决方法。
它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并发任务。
在 Mac OS X 10.6 雪豹中首次推出,也可在 iOS 4 及以上版本使用。
  GCD是一个替代诸如NSThread等技术的很高效和强大的技术。GCD完全可以处理诸如数据锁定和资源泄漏等复杂的异步编程问题。
GCD的工作原理是让一个程序,根据可用的处理资源,安排他们在任何可用的处理器核心上平行排队执行特定的任务。这个任务可以是
一个功能或者一个程序段。

来自百科(我是百科链接

好吧,但他有什么用呢?

  • GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉 GCD 想要执行什么任务,不需要编写任何线程管理代码
  • GCD 可用于多核的并行运算

  

2. GCD 任务和队列

GCD两个核心的概念:任务和队列

任务:

  就是执行操作的意思,就是你在线程中执行的那段代码,在 GCD 中是放在 block 中的。

  执行任务有两种方式:同步执行(sync)和异步执行(async)。

  两者的主要区别是:是否等待队列的任务执行结束还有就是是否具备开启新线程的能力。

  

  同步执行:1.同步添加任务到指定的队列中,在添加的任务结束之前,会一直等到队列里面的任务完成之后再继续执行。

       2.只能在当前线程中执行任务,没有开启新线程的能力。

  异步执行:1.异步添加任务到指定的队列中,就是直接插入到队列中,它不会做任何等待,可以直接执行任务。

       2.可以在新的线程中执行任务,有开启新线程的能力。(但是并不一定开启新线程)

队列:

  队列指执行任务的等待队列,即用来存放任务的队列。队列的本质是计算机中一种特殊的线性表,采用  先进先出 的原则,即新任务插入到队列的末尾,读取任务从队列头部开始读取。队列又分为两种:串行队列和并发队列。两者的主要区别是:执行顺序不同,开启线程数不同。(无论是串行队列还是并发队列两者都符合 FIFO“即先进先出”的原则。)

  串行队列(Serial Dispatch Queue):

      一个任务执行完毕后,再执行下一个任务,每次只执行一个任务。(只开启一个线程)

  并发队列(Concurrent Dispatch Queue):

      无需等待其他任务完成,可以多个任务同时执行。(可以开启多个线程)(注意:只有在异步(dispatch_async)函数下才有效)

 

3. GCD任务和队列的组合,以及创建方法

  GCD使用步骤也非常简单,1.创建队列   2.将任务添加到队列中

先说创建队列:

  
     //1   串行队列的创建方法
     dispatch_queue_t q_SERIAL = dispatch_queue_create("hzw", DISPATCH_QUEUE_SERIAL);
    
     //2   并发队列的创建方法
     dispatch_queue_t q_CONCURRENT = dispatch_queue_create("hzw", DISPATCH_QUEUE_CONCURRENT);

    
//    对于串行队列,GCD 提供了的一种特殊的串行队列:主队列(Main Dispatch Queue)。所有放在主队列中的任务,都会放到主线程中执行
    // 3  主队列的获取方法
    dispatch_queue_t queue_MAIN = dispatch_get_main_queue();
    
//    对于并发队列,GCD 默认提供了全局并发队列(Global Dispatch Queue)。
//    需要两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT或者传0。第二个参数苹果官方是这样解释的Flags that are reserved for future use。标记是为了未来使用保留的!所以这个参数传0即可。
    // 4  全局并发队列的获取方法
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

再说创建任务:

// 同步执行任务创建
dispatch_sync(queue, ^{
    // 同步执行任务代码
});
// 异步执行任务创建
dispatch_async(queue, ^{
    // 异步执行任务代码
});

  

  虽然说只有两步,但是队列有两种(并发队列和串行队列),还有刚刚提到的两种特殊的队列(全局并发队列,主队列),全局并发队列可以当做普通队列使用,也就是说现在一共有三种队列;任务有两种(同步任务和异步任务)于是组合起来就有六种方式,可以根据任务需求选择合适的方式使用。

  1. 同步执行 + 并发队列
  2. 异步执行 + 并发队列
  3. 同步执行 + 串行队列
  4. 异步执行 + 串行队列
  5. 同步执行 + 主队列
  6. 异步执行 + 主队列
 

4. GCD 的基本使用

4.1同步执行 + 并发队列

在当前线程中执行任务,不会开启新线程,执行完一个任务,再执行下一个任务。

- (void)syncConcurrent {
    NSLog(@"begin");
    
    dispatch_queue_t queue = dispatch_queue_create("hzw", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1---i=%d",i);
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务2---i=%d",i);
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3---i=%d",i);
        }
    });
    
    NSLog(@"end");
}

  

4.2异步执行 + 并发队列

  可以开启多个线程,任务同时执行。除了当前线程(主线程),系统又开启了3个线程,并且任务是交替/同时执行的。(异步执行具备开启新线程的能力。且并发队列可开启多个线程,同时执行多个任务)。所有任务是在打印的beginend之后才执行的。说明当前线程没有等待,而是直接开启了新线程,在新线程中执行任务(异步执行不做等待,可以继续执行任务)。任务执行时随机的,不确定的,每次打印出来执行的顺序都会不一样。

(void)asyncConcurrent {
    NSLog(@"begin");
    
    dispatch_queue_t queue = dispatch_queue_create("hzw", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 4; ++i) {
             NSLog(@"任务1---i=%d",i);
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 4; ++i) {
            NSLog(@"任务2---i=%d",i);
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 4; ++i) {
            NSLog(@"任务3---i=%d",i);
        }
    });
    
    NSLog(@"end");
}

  

4.3同步执行 + 串行队列

  不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务。

- (void)syncSerial {
    NSLog(@"begin");
    
    dispatch_queue_t queue = dispatch_queue_create("hzw", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
           NSLog(@"任务1---i=%d",i);
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
           NSLog(@"任务2---i=%d",i);
        }
    });
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
           NSLog(@"任务3---i=%d",i);
        }
    });
    
    NSLog(@"end");
}

  

4.4异步执行 + 串行队列

  会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务

- (void)asyncSerial {
    NSLog(@"begin");
    
    dispatch_queue_t queue = dispatch_queue_create("hzw", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务1---i=%d",i);
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
             NSLog(@"任务2---i=%d",i);
        }
    });
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
             NSLog(@"任务3---i=%d",i);
        }
    });
    
    NSLog(@"end");
}

  

4.5同步执行 + 主队列

  同步执行 + 主队列 在不同线程中调用结果也是不一样,在主线程中调用会出现死锁,而在其他线程中则不会。

  4.5.1同步执行 + 主队列 在主线程中执行

    线程卡住,不执行任务。因为我们在主线程中执行syncMain方法,相当于把syncMain任务放到了主线程的队列中。而同步执行会等待当前队列中的任务执行完毕,才会接着执行。那么当我们把任务1追加到主队列中,任务1就在等待主线程处理完syncMain任务。而syncMain任务需要等待任务1执行完毕,才能接着执行。

那么,现在的情况就是syncMain任务和任务1都在等对方执行完毕。这样大家互相等待,所以就卡住了,所以我们的任务执行不了,而且end也没有打印。

- (void)syncMain {
    
    
    NSLog(@"begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
             NSLog(@"任务1---i=%d",i);
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
           NSLog(@"任务2---i=%d",i);
        }
    });
    
    dispatch_sync(queue, ^{
        // 追加任务3
        for (int i = 0; i < 2; ++i) {
            NSLog(@"任务3---i=%d",i);
        }
    });
    
    NSLog(@"end");
}

  

 4.5.2同步执行 + 主队列 在其他线程中执行

    不会开启新线程,执行完一个任务,再执行下一个任务

//使用NSThread的这种创建方式会自动创建线程,然后方法会在该线程中执行
[NSThread detachNewThreadSelector:@selector(syncMain) toTarget:self withObject:nil];

  

4.6异步执行 + 主队列

  只在主线程中执行任务,执行完一个任务,再执行下一个任务。

  异步执行不会做任何等待,可以继续执行任务(可以看到先打印begin end 然后在执行任务,),任务是按顺序执行的。

- (void)asyncMain {
    NSLog(@"begin");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 3; ++i) {
            NSLog(@"任务1---i=%d",i);
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 3; ++i) {
            NSLog(@"任务2---i=%d",i);
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 3; ++i) {
           NSLog(@"任务3---i=%d",i);
        }
    });
    
    NSLog(@"end");
}

  

 
原文地址:https://www.cnblogs.com/huangzhenwei/p/9713152.html