您的位置:首页 > 移动开发 > IOS开发

IOS多线程的初步研究

2017-06-08 15:17 288 查看
 参考:http://www.cocoachina.com/bbs/read.php?tid-1710638.html
1.串行队列与并行队列
 
队列就是对线程的封装,在线程的基础上加上了任务容器。串行队列和并行队列,本质讲它们并无区别,但是它们允许的行为有很大区别。串行队列,你做完一个任务,才允许你从里面执行下一个任务。并行队列,无次限制,你可以一次性取完,你也可以一次性只取一个。串行队列里面在任意时间点,肯定是唯一的一个线程。而并行队列在任意时间点,可以有多个线程。
这两个队列都是先入先出队列,除非有特别情况(nsoperation)指定队列中任务的优先级,这个不再我们讨论范围之内,记住,不管是串行还是并行队列,只能一个个进队列,一个个出队列,即使是并行队列,也是一个个出不是有些人想想的并行可以一起出。宏观上讲,一个个出的速度很快,给你的错觉就是同一时间一起出来的,其实不是,并行队列也是一个个出来的
 
dispatch队列的生成可以有这几种方式:
 
(1).dispatch_queue_t queue = dispatch_queue_create("com.dispatch.serial",DISPATCH_QUEUE_SERIAL); //生成一个串行队列,队列中的block按照先进先出(FIFO)(排除队列优先级调整)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称,在调试程序时会非常有用,所有尽量不要重名了。
 
(2).dispatch_queue_t queue =dispatch_queue_create("com.dispatch.concurrent",DISPATCH_QUEUE_CONCURRENT); //生成一个并行执行队列(FIFO)(排除队列优先级调整),block被分发到多个线程去执行
 
(3).dispatch_queue_t queue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //获得程序进程缺省产生的并行队列,可设定优先级来选择高、中、低三个优先级队列。由于是系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。需要注意的是,三个队列不代表三个线程,可能会有更多的线程。并发队列可以根据实际情况来自动产生合理的线程数,也可理解为dispatch队列实现了一个线程池的管理,对于程序逻辑是透明的。
 
官网文档解释说共有三个并发队列,但实际还有一个更低优先级的队列,设置优先级为DISPATCH_QUEUE_PRIORITY_BACKGROUND。Xcode调试时可以观察到正在使用的各个dispatch队列。
 
(4).dispatch_queue_t queue = dispatch_get_main_queue(); //获得主线程的dispatch队列,实际是一个串行队列。同样无法控制主线程dispatch队列的执行继续或中断。
 
串行队列与并行队列的区别:
串行队列,你做完一个任务,才允许你从里面执行下一个任务,
并行队列,无次限制,你可以一次性取完,你也可以一次性只取一个
这两个队列都是先入先出队列,除非有特别情况(nsoperation)指定队列中任务的优先级,这个不再我们讨论范围之内,记住,不管是串行还是并行队列,只能一个个进队列,一个个出队列,即使是并行队列,也是一个个出不是有些人想想的并行可以一起出。宏观上讲,一个个出的速度很快,给你的错觉就是同一时间一起出来的,其实不是,并行队列也是一个个出来的。串行队列,可以理解为单车道。并行队列,可以理解为多车道。
 
2.同步与异步:
任务的执行分同步和异步。
同步,等待当前任务执行完毕。
异步,不用等待当前任务执行完毕。
 
我们可以使用dispatch_async或dispatch_sync函数来加载需要运行的block。
 
dispatch_async(queue,^{
 
  //block具体代码
 
}); //异步执行block,函数立即返回
 
dispatch_sync(queue,^{
 
  //block具体代码
 
}); //同步执行block,函数不返回,一直等到block执行完毕。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。
 
注意:对一个并行队列做同步操作,就如同对一个串行队列做异步操作
还记得同步操作的特点嘛?就是等待。并行队列,正常来讲如果做异步操作,会同时有多个线程去执行,因为不用等。不用等第一个任务完成才去执行第二个,但是如果是同步操作,那就必须要等第一个任务完成才开始执行第二个,即使你队列是并行的,但我做的是同步操作。
 
并行队列 :多个任务可以同时执行
串行队列 :一个任务执行完后,再执行下一个任务
dispatch_sync : 同步,不具备开启线程的能力
dispatch_async : 异步,具备开启线程的能力,不一定开
并行队列同步操作不开启新线程,串行执行任务;
串行队列异步操作,开启新线程,串行执行任务。

二者都是串行执行任务,只是开没开启新线程的区别. 
并行操作的时候,如果任务不多,在同一时间内,执行几个任务就几个线程。如果任务非常多,可能只开有限的线程,然后线程“复用”。

 
并行队列的异步操作本来就不保证执行顺序的先后
并行队列的同步操作就能确定任务执行顺序的先后
 
规律:如果使用dispatch_sync,如果执行dispatch_sync这句的上下文环境的“队列”,跟 dispatch_sync(queue,
^{  });的这个queue是同一个队列,并且该队列是串行队列,必然会死锁。
 
一个线程对应一个runloop,除了主线程的runloop默认打开无法关闭外,其它线程的runloop都是默认关闭的。一个runloop 可以有无数个循环,但这些循环都是结束一个再开起一个,串行的,
同步就是强制把提交的任务让当前上下文环境所处的线程来执行,首先你要知道,当前上下文所处的线程,肯定是在跑任务,肯定是在跑你的同步提交的代码。这个强调的意思就是:你上下文所处的线程现在正忙。突然,你同步提交了个任务。你强制让你这个线程去执行新任务。这时候它就必须断掉原来的任务(断掉的任务就是后面还未跑完的代码,尤其注意的是 }也算代码!!)去跑你这个同步提交的任务,所以这也就是我说的等待 等待你同步提交的任务执行完毕之后,再来执行下面没执行完的代码
 
从线程的角度来说阻塞的问题

假如上下文环境是主队列,然后你同步提交一个任务给主队列,根据以前说的,如果同步提交任务的目的地队列,跟现在上下文环境所处的队列是同一个队列,那么会产生死锁,为何呢?来分析

首先代码在主队列里安静的跑,跑着跑着碰到了你同步提交的代码。那么紧接着,就开始跑你的同步代码,首先你同步代码要求把任务提交给主队列,这没问题,但是问题在那?当你提交了你的任务之后,队列就要开始调度线程来执行,这没问题吧?主队列现在开始它的调度工作,他发现,它所能调度的只有一个线程,那就是主线程。为何?因为主队列里面只有一个线程在跑,一个串行队列不可能同时跑多个线程。现在静下来想一想,是不是所有的串行队列在任意一个时间点内,只有一个线程在跑?如果想不明白,那就想想主队列,是不是自始至终都是只有一个主线程在跑?(其实串行队列可以跑多个线程,但是这些线程是先后跑的,不能同时跑!!比如,123秒之内跑的是线程1,456秒之内跑的是线程2,纵向来看,串行队列确实跑了两个线程,但是横向来看,某个时间点之内有且同时只有一个线程在跑,至于为何会这样,原因就跟runloop有关,主队列之所以自始至终只有一个主线程,原因就是这个线程开启了runloop所以他不会退出,而串行队列同时只能跑一个线程,结合来看,那主队列只能跑一个主线程。至于其他串行队列,之所以前后可以跑不同的线程,原因就是默认的线程runloop并未打开,在这个线程的生命周期之内接到的任务,都会用这个线程跑,任务执行完,就退出了,因为没有runloop,那么下一次再来一个任务,队列只能再从线程池拿一个线程,至于这个线程是否跟上面那个刚刚退出的线程是一个线程,这个不一定,这要看队列的心情。。。)
好了。思维跳回来,你同步提交了任务给主队列,意思就是说:主队列,你必须给我把任务执行调度到我现在所处的线程上来!
现在,作为主队列,它很为难,为啥?因为它只有一个线程可用。。。那就是主线程。但是。。你要知道。。。主队列早已经把主线程的调度权给交出去了。。交给谁了?交给从一开始执行到你同步提交任务这段代码上了。你已经调度了一次主线程了,你无权在调它一次,因此,这时候就卡死在主队列调度线程这个点上,所以为什么说,阻塞并非是线程卡死,而是队列卡死,因为队列这个调度权的冲突,导致无法调度,从而导致线程已经断开了原来的执行,等待队列调度它去执行同步任务,而队列这时已经无权再调度一个任务了,这时候的情况是,线程停下来手头的工作,等待被召唤,而召唤师此时网费不够了,无权再玩游戏
 
好了做个总结:

从线程角度来讲:同步的意义就是:告诉提交到任务的目标队列,你必须马上立即开始你的调度权利,把我(同步操作)给你(目标队列)的任务(同步提交的任务)调度到我现在所处的线程上来运行,代码就是dispatch_sync(同步操作)(queue(目标队列),^block(同步提交的任务));

假如这时候queue是一个串行队列,那么在这个瞬间,他只有1个线程可以调度,假如这个线程没有被他调度,也就是说队列现在是空闲的,OK那没问题我直接调度一个线程,并且我调度的这个线程就是你跑dispatch_sync的这个线程立刻暂停原来的任务,转而来执行同步提交给我的任务,等我这个任务执行完毕之后,这个线程再继续原来的暂停的任务
假如queue已经调度这个任务去执行dispatch_sync这段代码,现在你又让我(queue)去调度我唯一能调度的任务去执行同步提交的任务,臣妾表示做不到啊。。。。。我只能对一个线程同时调度一次!
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: