网上对于ios多线程及gcd的文章有很多,我从多篇文章中截取了一些主要概念,当然也有一些疑问,并作了一些总结,写在下面,跟朋友们一起来看一下

进程和线程的区别

进程:

正在进行中的程序被称为进程,负责程序运行的内存分配;
每一个进程都有自己独立的虚拟内存空间.

线程:

(主线程最大占1M的栈区空间,每条子线程最大占512K的栈区空间)
线程是进程中一个独立的执行路径(控制单元);
一个进程中至少包含一条线程,即主线程;
可以将耗时的执行路径(如网络请求)放在其他线程中执行;
线程不能被杀掉,但是可以暂停/休眠一条线程.

Grand Central Dispatch(多线程的优化技术)GCD

是一套底层API,基于C语言开发的多线程机制,提供了新的模式编写并发执行的程序。

特点:
1.允许将一个程序切分为多个单一任务,然后提交到工作队列中并发或者串行地执行
2.为多核的并行运算提出了解决方案,自动合理的利用CPU内核(比如双核,四核)
3.自动的管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理,只需要告诉它任务是什么就行
4.配合Block,使得使用起来更加方便灵活

什么是Queue队列?

GCD使用了队列的概念,解决了NSThread难于管理的问题,队列实际上就是数组的概念,通常我们把要执行的任务放到队列中管理

特点:
    1.按顺序执行,先进先出
    2.可以管理多线程,管理并发的任务,设置主线程
    3.GCD的队列是任务的队列,而不是线程的队列

什么是任务?

任务即操作:你想要干什么,说白了就是一段代码,在GCD中,任务就是一个block
    任务的两种执行方式:
    同步执行:只要是同步任务,都会在当前的线程执行,不会另开线程
    异步执行:只要是异步任务,都会开启新线程,在开启的线程中执行

什么是串行队列?

依次完成每一任务

什么是并行队列?

好像所有的任务都是在同一时间执行的

都有哪些队列?

Main Queue(主队列,串行);全局队列(Global Queue);自己创建的队列(Queue)

从上面的概念以及gcd所解决的问题来看,使用GCD的时候就要开始转变观念了。现在我们需要考虑的只是任务,队列,队列间同步或异步的关系了。而不是考虑怎么开辟线程,怎么管理线程,所有关于线程的东西,我们都不需要考虑。整个程序完全就是由队列来自动管理了。首先,整个程序是由全局队列来管理,然后UI的刷新是由mainqueue管理,我们可以将我们的任务放到我们创建的队列中去,也可以放在主队列中,也可以放在全局队列中。

关于容易混淆概念的区分:

1.同步,异步,串行,并发

同步和异步代表会不会开辟新的线程(???对此说法有疑问)。串行和并发代表任务执行的方式。
同步串行和同步并发,任务执行的方式是一样的。没有区别,因为没有开辟新的线程,所有的任务都是在一条线程里面执行。
异步串行和异步并发,任务执行的方式是有区别的,异步串行会开辟一条新的线程,队列中所有任务按照添加的顺序一个一个执行,异步并发会开辟多条线程,至于具体开辟多少条线程,是由系统决定的,但是所有的任务好像就是同时执行的一样。

2 .主队列

主队列:专门负责调度主线程的任务,没有办法开辟新的线程。所以,在主队列下的任务不管是异步任务还是同步任务都不会开辟线程,任务只会在主线程顺序执行。
每一个应用程序都对应唯一一个主队列,直接GET即可,在多线程开发中,使用主队列更新UI;
主队列中的操作都应该在主线程上顺序执行,不存在异步的概念
主队列异步任务:现将任务放在主队列中,但是不是马上执行,等到主队列中的其它所有除我们使用代码添加到主队列的任务的任务都执行完毕之后才会执行我们使用代码添加的任务。 
主队列同步任务:阻塞主线程,所以不要这样写。原因:我们自己代码任务需要马上执行,但是主线程正在执行代码任务的方法体,因此代码任务就必须等待,而主线程又在等待代码任务的完成好去完成下面的任务,因此就形成了相互等待。整个主线程就被阻塞了。

主线程中也不绝对安全的 UI 操作

  • 在 GCD 中,使用 dispatch_get_main_queue() 函数可以获取主队列。调用 dispatch_sync() 方法会把任务同步提交到指定的队列
  • 注意一下队列和线程的区别,他们之间并没有“拥有关系(ownership)”,当我们同步的提交一个任务时,首先会阻塞当前队列,然后等到下一次 runloop 时再在合适的线程中执行 block
  • 在执行 block 之前,首先会寻找合适的线程来执行block,然后阻塞这个线程,直到 block 执行完毕。寻找线程的规则是: 任何提交到主队列的 block 都会在主线程中执行(即我们常说的主线程更新UI),在不违背此规则的前提下,文档还告诉我们系统会自动进行优化,尽可能的在当前线程执行 block。
  • 即使是在主线程中执行的代码,也很可能不是运行在主队列中(反之则必然)
  • GCD 死锁的充分条件是:“向当前队列重复同步提交 block”。(???对此说法有疑问)从原理来看,死锁的原因是提交的 block 阻塞了队列,而队列阻塞后永远无法执行完 dispatch_sync(),可见这里完全和代码所在的线程无关
  • 另一个例子也可以证明这一点,在主线程中向一个串行队列同步的派发 block,根据上文选择线程的原则,block 将在主线程中执行,但同样不会导致死锁:

    dispatch_queue_t queue = dispatch_queue_create("com.kt.deadlock", nil); dispatch_sync(queue, ^{ NSLog(@"current thread = %@", [NSThread currentThread]); }); // 输出结果: // current thread = <NSThread: 0x7fa7fb403e90>{number = 1, name = main}

iOS多线程中,队列和执行的排列组合结果分析

  1. 开不开线程,取决于执行任务的函数,同步不开,异步开。(???对此说法有疑问)
  2. 开几条线程,取决于队列,串行开一条,并发开多条(异步) (???对此说法有疑问)
  3. 主队列: 专门用来在主线程上调度任务的"队列",主队列不能在其他线程中调度任务!
  4. 如果主线程上当前正在有执行的任务,主队列暂时不会调度任务的执行!主队列同步任务,会造成死锁。原因是循环等待
  5. 同步任务可以在多个异步任务前,指定一个同步任务,让所有的异步任务,等待同步任务执行完成,这是依赖关系。
  6. 全局队列:并发,能够调度多个线程,执行效率高,但是相对费电。 串行队列效率较低,省电省流量,或者是任务之间需要依赖也可以使用串行队列。
  7. 也可以通过判断当前用户的网络环境来决定开的线程数。WIFI下6条,3G/4G下2~3条。

    GCD优点:

    1> 通过GCD,开发者不用再直接跟线程打交道,只需要向队列中添加代码块即可.

    2> GCD在后端管理着一个线程池,GCD不仅决定着代码块将在哪个线程被执行,它还根据可用的系统资源对这些线程进行管理,从而让开发者从线程管理的工作中解放出来;通过集中的管理线程,缓解大量线程被创建的问题.

    3> 使用GCD,开发者可以将工作考虑为一个队列,而不是一堆线程,这种并行的抽象模型更容易掌握和使用.

苹果官方给出的GCD队列示意图:

hZ5iJigxkgZNflhW.png

093608_BOVc_1774273.png

从中可以看出: GCD公开有5个不同的队列:运行在主线程中的主队列,3个不同优先级的后台队列以及一个优先级更低的后台队列(用于I/O).

自定义队列:串行和并行队列.自定义队列非常强大,建议在开发中使用.
在自定义队列中被调度的所有Block最终都将被放入到系统的全局队列中和线程池中.

提示:

不建议使用不同优先级的队列,因为如果设计不当,可能会出现优先级反转,即低优先级的操作阻塞高优先级的操作.

不同观点,理解GCD死锁

关于GCD死锁的文章,有一些非常明显的错误,比如:认为死锁的原因是线程阻塞造成的,这是非常大的误解,GCD死锁的原因是队列阻塞,而不是线程阻塞!(这种说法比较新颖,文章的解释也比较详细,大伙对这种说法怎么看呢,欢迎评论)

造成GCD死锁,是由于同时具备以下两点因素:

  1. GCD函数未返回,会阻塞正在执行的任务
  2. 队列的执行室容量太小,在执行室有空位之前,会阻塞同一个队列中在等待的任务

关于同步异步:

dispatch_sync是同步函数,不具备开启新线程的能力,交给它的block,只会在当前线程执行,不论你传入的是串行队列还是并发队列,并且,它一定会等待block被执行完毕才返回。

dispatch_async是异步函数,具备开启新线程的能力,但是不一定会开启新线程,交给它的block,可能在任何线程执行,开发者无法控制,是GCD底层在控制。它会立即返回,不会等待block被执行。
注意:以上两个知识点,有例外,那就是当你传入的是主队列,那两个函数都一定会安排block在主线程执行。记住,主队列是最特殊的队列

参考资料
关于iOS多线程

线程与进程的区别于联系

理解GCD死锁

五个案例让你明白GCD死锁

你可能感兴趣的内容
0条评论
ER

erjan

这家伙太懒了,什么都没留下
Owner