美文网首页
GCD篇(1)

GCD篇(1)

作者: 哲逗年 | 来源:发表于2018-04-17 15:39 被阅读0次

GCD的队列有两种,一种是串行队列,一种是并发队列。

并行与并发的区别

并发(concurrency)

并发
1.并发的实质是一个物理CPU(也可以多个物理CPU)在若干道程序(或线程)之间多路复用,并发性是对有限物理资源强制行使多用户共享以提高效率
2.微观角度:所有的并发处理都有排队等候、唤醒、执行等这样的步骤,在微观上他们都是序列被处理的,如果是同一时刻到达的请求(或线程)也会根
  据优先级的不同,而先后进入队列排队等候执行。
3.宏观角度:多个线程同时达到的请求(或线程)在宏观上看就像是同时在被处理。
4.通俗点讲,并发就是只有一个CPU资源,程序(或线程)之间要竞争得到执行机会。图中的第一个阶段,在A执行的过程中B、C都不会执行,因为这段时
  间内这个CPU资源被A竞争道了,同理,第二个阶段只有B在执行,第三个阶段只有C在执行。其实,并发过程中,A、B、C并不是同时在进行的
 (微观角度)。但又是同时进行的(宏观角度)。

并行(parallelism)

并行
1.指两个或两个以上事件(或线程)在同一时刻发生,是真正意义上的不同事件或线程在同一时刻,在不同CPU资源上(多核),同时执行。
2.并行,不存在像并发那样竞争、等待的概念。
3.图中,A、B、C都在同时运行(微观、宏观)。

通过多线程实现并发,并行

1.java中的Thread类定义了多线程,通过多线程可以实现并发或并行
2.在CPU比较繁忙,资源不足的时候(开启了很多进程),操作系统只为一个含有多线程的进程分配仅有的CPU资源,这些线程就会为自己尽量多抢时间片,
  这就是通过多线程实现并发,线程之间会竞争CPU资源争取执行机会。
3.在CPU资源比较充足的时候,一个进程内的多线程,可以被分配到不同的CPU资源,这就是通过多线程实现并行。
4.至于多线程实现的是并发还是并行?上面所说,所写多线程可能被分配到一个CPU内核中执行,也可能被分配到不同CPU执行,分配过程是操作系统所为,不可人为控制。所以,多线程是并发还是并行的,都有可能。

以上并行并发的原文来自这里

串行队列

任务按队列里的添加先后顺序执行,先进先出(FIFO),前一个任务执行完再开始执行下一个任务。(我们开发中主线程队列就是一个串行队列,所以我们经常在主线程写的一般任务(不考虑多线程),都是顺序执行的)。

注意:一个串行队列里只有一个线程。

1.主队列

dispatch_get_main_queue()

2.自定义队列(串行)

// DISPATCH_QUEUE_SERIAL实际上是指向NULL的宏
dispatch_queue_create("queue_name", DISPATCH_QUEUE_SERIAL)

并发队列

任务会在这个队列中新开线程,并发同时执行(无序)。

GCD使用常伴有dispatch_sync和dispatch_async,这就是同步执行和异步执行。
1.全局队列

dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

2.自定义队列

dispatch_queue_create("queue_name", DISPATCH_QUEUE_CONCURRENT)    

同步和异步

同步执行:任务都在当前线程中执行,执行过程中会阻塞当前线程。
异步执行:任务会开辟新的线程,并在新的线程中执行,不会阻塞当前线程。

注意
  • 同步执行没有开启新线程的能力,所有的任务都只能在当前线程执行。
  • 异步执行有开启新线程的能力,但是,有开启新线程的能力,也不一定会利用这种能力,也就是说,异步执行是否开启新线程,需要具体问题具体分析。
  • 串行队列中的任务只会放到同一线程中去执行。
  • 并发队列中任务会放到不同的线程中去执行。

综上所诉,队列有2种,执行方式有2种,那么他们互相组合会是什么情况呢?

很显然,他们可以组合成4种情况:

  • 1)、串行同步队列:任务都在当前线程执行(同步),并且顺序执行(串行);
  • 2)、串行异步队列:任务都在开启的新的子线程中执行(异步),并且顺序执行(串行);
  • 3)、并发同步队列:任务都在当前线程执行(同步),但是是顺序执行(并没有体现并发的特性)
  • 4)、并发异步队列:任务在开启的多个子线程中执行(异步),并且是同时执行的(并发)
GCD
验证
1.串行同步队列
        // 串行同步
    dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        NSLog(@"task1");
        NSLog(@"task1--%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"task2");
        NSLog(@"task2--%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"task3");
        NSLog(@"task3--%@",[NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        NSLog(@"task4");
        NSLog(@"task4--%@",[NSThread currentThread]);
    });  

输出结果:

 task1
 task1--<NSThread: 0x100602bc0>{number = 1, name = main}
 task2
 task2--<NSThread: 0x100602bc0>{number = 1, name = main}
 task3
 task3--<NSThread: 0x100602bc0>{number = 1, name = main}
 task4
 task4--<NSThread: 0x100602bc0>{number = 1, name = main}

分析:任务是在当前线程(当前是主线程)顺序执行的。这也验证了
串行同步队列:任务都在当前线程执行(同步),并且是顺序执行(串行)

这里需要注意的是代码直接写在viewDidLoad里,主队列也是也一个串行队列,但在主线程中使用主队列同步执行会造成死锁。另外,若在viewDidLoad新开一个子线程,去执行代码,结果是同样可以验证的:

// 开启新线程
 [NSThread detachNewThreadWithBlock:^{
 
 // 串行同步
 dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
 
 dispatch_sync(queue, ^{
     NSLog(@"task1");
     NSLog(@"task1--%@",[NSThread currentThread]);
 });
 
 dispatch_sync(queue, ^{
     NSLog(@"task2");
     NSLog(@"task2--%@",[NSThread currentThread]);
 });
 
 dispatch_sync(queue, ^{
     NSLog(@"task3");
     NSLog(@"task3--%@",[NSThread currentThread]);
 });
 
 dispatch_sync(queue, ^{
     NSLog(@"task4");
     NSLog(@"task4--%@",[NSThread currentThread]);
 });
 
}];
 
// 输出结果:
task1
task1--<NSThread: 0x1028004b0>{number = 2, name = (null)}
task2
task2--<NSThread: 0x1028004b0>{number = 2, name = (null)}
task3
task3--<NSThread: 0x1028004b0>{number = 2, name = (null)}
task4
task4--<NSThread: 0x1028004b0>{number = 2, name = (null)}

死锁情况:

        dispatch_sync(dispatch_get_main_queue(), ^{// 在主队列里执行同步操作会死锁
            NSLog(@"task1");
            NSLog(@"task1--%@",[NSThread currentThread]);
    });
2.串行异步队列
dispatch_queue_t queue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
    NSLog(@"task1");
    NSLog(@"task1--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"task2");
    NSLog(@"task2--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
    NSLog(@"task3");
    NSLog(@"task3--%@",[NSThread currentThread]);
});
NSLog(@"task4");
NSLog(@"task4--%@",[NSThread currentThread]);

输出结果:

task4
task1
task1--<NSThread: 0x100582fd0>{number = 2, name = (null)}
task4--<NSThread: 0x100509fa0>{number = 1, name = main}
task2
task2--<NSThread: 0x100582fd0>{number = 2, name = (null)}
task3
task3--<NSThread: 0x100582fd0>{number = 2, name = (null)}  

分析:主线程异步调用,我们先分析加到队列里的task任务1、2、3,确实是在开辟了的新线程<NSThread: 0x100582fd0>{number = 2, name = (null)} 上顺序执行的,关于task4,由于是异步的,它也没加入到queue,啥时候输出就看电脑心情了。

验证结果:
串行异步队列:任务都在开启的新的子线程中执行(异步),并且顺序执行(串行)

这里需要注意,由于新创建了串行线程,所以任务会在新开启的线程上执行,若是直接在主队列异步调用,任务执行都在主线程上。

3.并发同步队列
dispatch_queue_t queue = dispatch_queue_create("concurrentqueue", D

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

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

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

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

输出结果:

task1
task1--<NSThread: 0x102a03400>{number = 1, name = main}
task2
task2--<NSThread: 0x102a03400>{number = 1, name = main}
task3
task3--<NSThread: 0x102a03400>{number = 1, name = main}
task4
task4--<NSThread: 0x102a03400>{number = 1, name = main}

分析:任务是当前线程(是主线程没有开辟新线程)顺序执行的,跟串行同步一样,虽是并发队列,却不能并发。得到验证结果:

并发同步队列:任务都在当前线程执行(同步),但是是顺序执行的(并没有体现并发的特性)

4.并发异步队列
 //并发异步队列
 dispatch_queue_t queue = dispatch_queue_create("concurrentqueue",DISPATCH_QUEUE_CONCURRENT);

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

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

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


 NSLog(@"task4");
 NSLog(@"task4--%@",[NSThread currentThread]);

输出结果:

task4
task1
task2
task3
task2--<NSThread: 0x1030055a0>{number = 2, name = (null)}
task4--<NSThread: 0x100507190>{number = 1, name = main}
task3--<NSThread: 0x100406450>{number = 3, name = (null)}
task1--<NSThread: 0x103200000>{number = 4, name = (null)}

分析:先看task4吧,没有加入队列,所以肯定是在主线程执行的,由于异步,啥时候执行还是要看电脑执行……我们看加入到并发队列里的任务1、2、3,在不同的线程中无序执行,每个任务都开辟了新的线程去执行,并且执行顺序是无序的,体现了并发的特性。所以我们经常也是使用这种方式做一些需求。验证结果:

并发异步队列:任务在开辟的多个子线程中执行(异步),并且是同时执行的(并发)

番外:做个手抄,加深印象,原文请移步这里

相关文章

  • iOS Objective-C GCD之函数篇

    iOS Objective-C GCD之函数篇 1. GCD 中函数简介 在上一篇队列篇中我们简要的介绍了GCD中...

  • GCD篇(1)

    GCD的队列有两种,一种是串行队列,一种是并发队列。 并行与并发的区别 并发(concurrency) 并行(pa...

  • iOS-多线程篇—GCD介绍

    iOS开发多线程篇—GCD介绍一、简单介绍1.什么是GCD?全称是Grand Central Dispatch,可...

  • 深入理解GCD之dispatch_queue

    1.前言 上一篇我们介绍了GCD的结构体,这一篇我们着重看一下GCD中队列的构成。队列是我们在使用GCD中经常接触...

  • iOS GCD&&多线程

    iOS GCD&&多线程 基础篇 GCD用途 GCD 是 Grand Central Dispatch 的缩写。 ...

  • 多线程编程之GCD(二)

    承接上一篇:多线程编程之GCD(一) 三、系统Dispatch_Queue GCD提供了两个系统的Queue:1、...

  • iOS面试攻略,浅谈多线程开发2(GCD)

    之前写了一篇多线程开发的iOS 面试攻略,浅谈多线程开发1但是没有跟大家聊GCD 就是想把GCD单独放在一篇文章跟...

  • iOS 笔记---GCD篇(1)

    目前在iOS开发中,用到多线程的时候自然就想到用GCD,因为方便快捷,代码清晰。因此记录一下。 介绍 什么是GCD...

  • 停不下来的多线程

    GCD 扫盲篇 Grand Central Dispatch 基础教程Swift:Part 1 & Part2--...

  • 多线程系列文章

    GCD基础篇 - 同步、异步,并发、并行的理解 GCD中级篇 - dispatch_group的理解及使用 dis...

网友评论

      本文标题:GCD篇(1)

      本文链接:https://www.haomeiwen.com/subject/ixrukftx.html