调度任务是串行执行还是java8 并行执行任务,前一次调度还未完成

基于DAG图的任务调度算法
以前的任务分配或任务调度算法很少认真地考虑任务间的时序关系,而这在任务集的分布并行计算中经常出现。因此,本文中给出了一个基于DAG图的调度策略,图中严格规定了任务间的时序关系。经过该策略调度后,不仅可以保证任务集完全符合逻辑顺序地并发执行,还可以使每个任务以较早的时间开始执行,从而达到提高系统效率的作用。我们首先假定任务集中的每个任务的执行时间、任务间的时序关系、任务间的通信量以及可利用的处理机数量都是已知的,所以这个调度策略属于静态调度策略。它的主要步骤分为两个部分:①对每个任务计算出它的最早开始时间(在计算过程中对任务聚集),这个最早开始时间是指在有充分可利用资源(在本文中主要是处理机资源和通信信道)和DAG图限制下,每个任务最早能够在什么时间开始执行。在实际执行过程中,每个任务的开始时间不可能早于其最早开始时间。因此,如果在任务集中的每个任务都以最早开始时间开始执行,那么,这个调度方案就是最优调度方案。②根据步骤一的任务聚...&
(本文共4页)
权威出处:
1引言网格任务按照其是否包含子任务可以划分为独立任务和关联任务.独立任务在调度的时候,根据每个任务的优先级分配资源,无需考虑任务的依赖关系;关联任务则通过有向无环图DAG(Direct Acyclic Graph)[1]表示其子任务间的依赖关系,调度时优先调度图中的上层任务,然后调度低层的任务,从而避免出现死锁.多个关联任务间的依赖是一个不可忽略的因素,传统算法将其视为元任务来考虑,限制了对任务粒度的进一步划分,从而降低了任务调度的性能.本文在对复杂任务进行划分,并用DAG图描述任务间互联的基础上,查找关联任务的关键路径,优先调度关键路径上的任务,并对剩余任务进行合理优化.该算法利用网格环境并行性高的特点,提高资源的利用率.2相关工作目前网格环境的关联任务调度算法都是从传统的同构集群环境中演变过来的.在同构环境下,Sabuncuoglu等人使用bearn搜索技术在两台机器上调度关联任务[11];Woeginger等人提出一种启发...&
(本文共5页)
权威出处:
近年来,集群系统成为高性能计算的主流平台,在此系统上进行并行计算蕴涵着巨大的计算潜力,调度是挖掘这些计算能力的关键技术。在并行系统中,应用任务被划分为多个子任务,有向无环图(DAG)因能反映并行系统中各子任务以及这些任务间的依赖关系而被广泛采用,本文研究基于DAG图的集群系统任务调度问题,具体如下:(1)对于同构集群下的调度问题,采用表调度技术解决。分析现有表调度算法的不足,在此基础上提出基于动态关键路径的全局调度算法GDCP。该算法在调度任务选择阶段准确地赋予任务优先级,并结合全局搜索策略选择处理机。实验及数据分析表明,该算法有效地提高了调度性能。(2)对于异构集群下的调度问题,采用遗传调度算法解决。针对传统遗传调度算法在种群初始化方式上的缺陷,提出异构系统中改进的遗传调度算法ISGA。该算法利用任务的属性值来构造染色体,得到优质的初始种群。实验及数据分析表明,该算法可以获得较好的调度质量。(3)将本文研究成果应用于与国家电网...&
(本文共68页)
权威出处:
在当今的网络并行计算环境中,并行任务调度已经成为并行处理和高性能计算领域中极其重要的关键技术,不恰当的调度甚至会抵消任务并行化所带来的收益。基于此,本文研究了一种以带节点权值和边权值的有向无环图来表示并行任务的DAG调度问题。一般情况下,这种DAG调度是个NP完全问题。国内外的研究学者在该领域进行了广泛而深入的研究。但是随着网络硬件技术和处理器技术的飞速发展,该领域仍然存在不少亟待解决的关键问题。在这种新形势下,本文主要研究了其中的三个关键技术,它们是:其一,针对非线性聚簇下怎样高效调度独立任务问题,本文提出了一种基于最大并行度的独立任务调度算法MPD;其二,针对某些调度算法虽然性能良好但复杂度高,或者某些调度算法虽然复杂度低但性能不佳的问题,本文提出了一种在性能和复杂度间进行折衷的DAG调度算法EZDCP,从而使得DAG调度算法更加实用;最后本文还提出了一个较为系统性的DAG粒度理论,并且根据fork和join图,本文用数学方...&
(本文共59页)
权威出处:
网络计算环境日益成为一种不受地域限制的廉价的超级计算环境。任务调度是目前网格计算中一个热点研究的问题,是能够高效使用网格资源的重要保证。本文在研究电力网格体系结构,网格任务特点等的基础上,针对网格的异构特性以及网格中任务间的依赖性,提出两种基于DAG的并行任务调度算法。这两种算法能够方便地应用于其他计算网格。鉴于电力系统对计算任务的实时性、高效性的要求,如果把网格技术运用到电力系统计算中来,可以充分发挥网格计算的优势,构建了完全异构性的电力网格开发平台,以满足电力系统高性能计算的要求。电力网格中的应用服务可以分解为若干个互有依赖关系的子任务,这些子任务可以用DAG图表示。对网格中任务的调度问题可以转化为DAG的调度问题。网格中存在着机器计算能力的异构性,表现在电力网格中的某些子任务只能在部分计算节点上运行,而某些子任务可以被同时分派到多个计算节点上并行执行。本文提出了任务可计算性和机器可计算性这两个概念来量化这种异构性。针对网格...&
(本文共71页)
权威出处:
1引言网格计算是继因特网、Web之后的第三大技术浪潮,是当前的一个研究热点.Ian Foster定义网格计算为动态多机构虚拟组织中的一个协调的共享资源和解决问题的过程.任务调度是网格计算的一个重要组成部分,已被证明是NP完全问题,适合使用启发式方法.网格环境下的任务调度包括元任务的调度和依赖任务的调度,元任务独立于其它任务,而依赖任务之间存在先后依赖关系.当前的许多网格任务调度算法是建立在元任务基础上的调度,而考虑依赖任务的情况较少,因而具有一定的局限性,如最小最小算法(Min-Min)、最大最小算法(Max-Min)、最大时间跨度算法(Max-Int)、快速贪吃算法(Fast-Greedy)[1]等等.对于依赖任务的调度,研究者基于不同的模型提出了不同的网格调度算法.在传统的模型中,有向无环图(Directed Acyclic Graph,DAG)是典型代表,在此基础上提出了许多任务调度算法,如分代算法(Gen-eration...&
(本文共5页)
权威出处:
扩展阅读:
CNKI手机学问
有学问,才够权威!
出版:《中国学术期刊(光盘版)》电子杂志社有限公司
地址:北京清华大学 84-48信箱 大众知识服务
京ICP证040431号&
服务咨询:400-810--9993
订购咨询:400-819-9993
传真:010-查看: 91703|回复: 6
Spark 作业调度--job执行方式介绍
主题帖子积分
本帖最后由 hyj 于
19:01 编辑
问题导读:
1.由不同线程提交的多个“jobs”(Spark actions)是否可以同时运行
2.线程的含义什么?
3.Java main函数是进程还是线程?
4.应用程序启用Spark独立部署模式,job是否并发执行?
1.Spark是否并发分析
本文是针对spark作业执行方式是否并发解惑的一篇文章。spark有三种作业调度方式,那么他们的调度方式什么?这里在下文的基础上分析一下:
首先我们知道不同的用户,对于一个集群可能会同时不断的提交作业,那么这些job是怎么执行的,这里困惑了不少刚接触spark的同学。其实无论是那种部署方式,他们都是有可能并发执行的,也就是可能是同时执行的。
下面引用下文的内容:
由不同线程提交的多个“jobs”(Spark actions)可以同时运行
这里我产生了疑问,什么是线程,main()函数是进程还是线程。引用下面内容
main是一个线程也是一个进程,一个java程序启动后它就是一个进程,进程相当于一个空盒,它只提供资源装载的空间,具体的调度并不是由进程来完成的,而是由线程来完成的。一个java程序从main开始之后,进程启动,为整个程序提供各种资源,而此时将启动一个线程,这个线程就是主线程,它将调度资源,进行具体的操作。
对于不同用户提交的.jar是可以理解为一个线程的,因此从下文得知
由不同线程提交的多个“jobs”(Spark actions)可以同时运行
无论是那种部署方式,我们的spark程序都是可以并发执行的。详细可以看下文。
2.Spark 作业调度
Spark有几个在计算中调度资源的工具。
第一需要记得,正如集群模式概述中描述的那样,每个Spark应用中(SparkContext实例)都运行着一组独立的执行进程。Spark运行在的集群管理器提供了应用间调度的工具。
第二,在每个Spark应用中,由不同线程提交的多个“jobs”(Spark actions)可以同时运行。在处理网络请求的应用中这很常见,比如Shark服务器就以这种方式运行。Spark有一个调度均衡器在每个SparkContext中调度资源。
应用程序之间的调度
每个运行在集群上的Spark应用程序都能得到一个独立的JVM虚拟机,而JVM仅仅用于应用程序运行任务和存储数据。如果多用户需要共享你的集群,可以通过集群管理器配置不同的选项来分配资源。
在集群管理器中最简单有效的方式就是静态区分资源。使用此方法,每个应用程序在整个的生命周期中都可以得到一个最大数量的资源。这种方式被用于Spark的standalone和YARN模式中,同样也用于coarse-grained Mesos mode模式。根据集群的类型,可以通过下面的配置来分配资源。
1.Standalone mode:默认情况下,应用程序启用Spark独立部署模式,这种模式按照FIFO(先进先出)的顺序执行,每个应用程序都会尝试使用所有可用的节点。你可以通过spark设置来限制应用程序的节点数。例如:你可以启动一个有10核并长时间运行的服务器,并允许每个用户通过shells使用20核心。最后,除了控制核心外,每个应用程序的spark执行存储器可以控制自己的内存使用。
2.Mesos:在独立部署模式中,要在Mesos上使用静态分区,需要设置spark.mesos.coarse 系统属性为true,另外,可选项设置spark.cores.max可以限制每个应用程序的共享资源数。你也可以配置spark.executor.memory来控制执行器的内存。
3.YARN:num-workers选项用于在Spark YARN端分配集群上workers数量,尽管worker-memory和worker-cores可以控制每个worker的资源分配。
Mesos上的第二个可用选项是动态共享CPU内核。在这种模式下,每个Spark应用程序仍然分配有一个固定和独立的内存(通过spark.executor.memory来设置),当这个应用程序没有在机器上执行任务的时候,其他的应用程序就可能在这些内核上运行任务。当你期望大量但不是过度活跃应用程序的时候,这种模式是非常有用的,例如独立用户中的shell会话。然而,它却伴随着一个不可预知的潜在危险,这是因为当它需要执行任务的时候,在节点上需要耗费一段时间重新获得CPU核心资源。使用这种模式,不需要设置spark.mesos.coarse为true,只需要简单的使用amesos://URL。
请注意,所有的模式目前提供跨应用程序内存共享。如果你喜欢通过这种方式共享数据,我们推荐运行单一服务器的应用程序能够提供多个请求,可以通过查询相同的RDDs得到。例如,Shark JDBC服务器以这种方式进行SQL查询。在将来的版本中,内存中的存储系统,如 Tachyon将会提供另外的一种方式来共享RDDs。
应用中的调度
在给定的Spark应用(已实例化SparkContext)中,如果在不同线程中,多个并行的工作可以同时运行。我们所说的“工作”,其实是一个Spark动作(如保存,收集等)或是任何想需要评估动作的任务。Spark的任务调度员是多线程安全的,它也支持这个用例来使应用服务多个请求(多用户查询).
默认的,Spark调度员是按照FIFO(先进先出)的形式来运行工作的。每一个工作被分为多个“阶段”(如,map和reduce语句),对于所有可用的资源中第一个工作优先级最高,这个工作阶段中的任务会被启动,之后是第二个,依次类推。如果集群不需要队列头中的工作,后面的工作将被立刻启动,如果队列头的工作很大,后面的工作可能大大地推迟。
启动Spark0.8,它可以在两个作业之间配置共享调度。Spark负责在作业之间轮换分配调度,所以,所有的作业都能得到一个大致公平的共享的集群资源。这就意味着即使有一个很长的作业在运行,花费时间较少的作业在提交之后,仍然能够立即获得资源,并且能够有很好的响应,而不是需要等待那个很长的作业运行完之后才能运行。这种模式最适合多用户设置。
要启用公平作业调度,在创建一个SparkContext之前,需要简单的配置spark.scheduler.mode为FAIR:
System.setProperty(&spark.scheduler.mode&, &FAIR&) 复制代码
公平的调度池
公平调度可以支持在池中将工作分组,而且为不同的池可以设置不同的调度选项(如,权重)。这样可以很有用的为更多重要的工作创建一个“高优先级”池,举例,将每一个用户的工作一起分组,不管有多少并发工作也让每个用户平等的分享 ,以这种方式代替了平分给定的工作。这种方式是模仿Hadoop公平调度。
如果没有设置,新提交的工作将进入默认池中,但是工作池可以在线程中用spark.scheduler.pool来给SparkContent添加“本地属性”并提交。如下:
// 假设context是你SparkContext中的变量
context.setLocalProperty(&spark.scheduler.pool&, &pool1&)复制代码
在设置了本地属性之后,所有的在这个线程(在这个线程中调用 RDD.save,count,collect等)的工作提交将会用这个池来命名。这样同一个用户可以让每个线程容易的执行多个工作。如果你想要清除线程相关的池,简单调用如下:
context.setLocalProperty(&spark.scheduler.pool&, null) 复制代码
调度池中的默认行为
默认的,每个池都会平等的分享集群(在默认的池中每一个工作也是平等分享的),但在每一个池中,工作是按照FIFO(先进先出)顺序。比如,如果你给每一个用户创建一个池,这就意味着每一个用户都平等的分享一个集群,这样每一个查询都是按顺序查询的。
配置调度池
通过配置文件可以修改调度池的属性。每个调度池都支持3个属性。
schedulingMode:该属性的值可以是FIFO或者FAIR,用来控制作业在调度池中排队运行(默认情况下)或者公平分享调度池资源。
weight:控制调度池在集群之间的分配。默认情况下,所有调度池的weight值都是为1。例如:如果你指定了一个调度池的值为2,那么这个调度池就比其它调度池多获得2倍的资源。设置一个更高的weight值,例如1000,就可以实现线程池之间的优先权——实际上,weight值为1000的调度池无论什么时候作业被激活,它都总是能够最先运行。
minShare:除了一个整体的权重,如果管理员喜欢,可以给每个调度池指定一个最小的shares值(也就是CPU的核数目)。公平调度器通过权重重新分配资源之前总是试图满足所有活动调度池的最小share。在没有给定一个高优先级的其他集群中,minShare属性是另外的一种方式来确保调度池能够迅速的获得一定数量的资源(例如10核CPU),默认情况下,每个调度池的minShare值都为0。
可以通过XML文件来设置pool属性,和配置公平调度的xml模板文件一样,只需要设置spark.scheduler.allocation.file的属性:
System.setProperty(&spark.scheduler.allocation.file&, &/path/to/file&) 复制代码
对于每个池,XML文件的格式是一个简单的&pool&元素,可以在这个元素中设置各种不同元素。例如:
&?xml version=&1.0&?&
&allocations&
&&&pool name=&production&&
& & &schedulingMode&FAIR&/schedulingMode&
& & &weight&1&/weight&
& & &minShare&2&/minShare&
&&&/pool&
&&&pool name=&test&&
& & &schedulingMode&FIFO&/schedulingMode&
& & &weight&2&/weight&
& & &minShare&3&/minShare&
&&&/pool&
&/allocations&复制代码这个完整的例子也可以适用到对公平调度的xml模板文件配置。请注意,任何没有在xml文件中配置的池,都会有一个默认配置值(scheduling mode 值是FIFO,weight值为1,minShare值为0)。
欢迎加入about云群 、 ,云计算爱好者群,关注
主题帖子积分
我现在有点迷糊,请看如下代码:
[Scala] 纯文本查看 复制代码object JobScheduling {
def main(args: Array[String]) {
System.setProperty(&spark.scheduler.mode&, &FAIR&)
val conf = new SparkConf().setAppName(&BaseLineDemo&).setMaster(&local[*]&)
val sc = new SparkContext(conf)
val ssc = new StreamingContext(sc, Seconds(5))
ssc.checkpoint(&/tmp/checkpoint&)
// 通过TCP连接本机7788端口 获得输入流
val logs = ssc.receiverStream(new UdpReceiver(9514, &UTF-8&))
logs.foreachRDD(rdd =&{
rdd.foreach(event =& {
// action-1
println(s&---------------${event}&)
Thread.sleep(100)
logs.foreachRDD(rdd =&{
// action-2
rdd.foreach(event =& {
println(s&+++++++++++++++${event}&)
ssc.start()
ssc.awaitTermination()
action-1&&action-2&&应该就是两个job。那么这两个job是串行执行的 还是并行执行的?
& & 在每个Spark应用中,由不同线程提交的多个“jobs”(Spark actions)可以同时运行。
在spark中怎么使用多线程提交jobs? 使用 sparkContent.runJob 么? 能否给个例子??
主题帖子积分
高级会员, 积分 2673, 距离下一级还需 2327 积分
高级会员, 积分 2673, 距离下一级还需 2327 积分
我现在有点迷糊,请看如下代码:
[mw_shl_code=scala,true]object JobScheduling {
spark internal - 作业调度
主题帖子积分
我现在有点迷糊,请看如下代码:
[mw_shl_code=scala,true]object JobScheduling {
action-1,action-2的确是两个不同的job,当action触发时将会提交job,这两个job的运行将在调度模式下依次执行,即串先行执行。执行的顺序具体看你配置的job调度策略,默认为FIFO,也可以配置成公平调度策略,在公平调度策略中可配置job执行的权重,权重越大越优先执行。每一个job提交后将会划分stage,每个stage中会有多个任务,每个stage中的任务在worker节点在线程池中并行处理。
主题帖子积分
我现在有点迷糊,请看如下代码:
[mw_shl_code=scala,true]object JobScheduling {
在多个线程中可以提交job,但是提交的job到spark集群中也是遵守调度规则的,即每一个job也是顺序执行的。
主题帖子积分
我现在有点迷糊,请看如下代码:
[mw_shl_code=scala,true]object JobScheduling {
比如说你要在10个线程中提交Job,像下面一样
for(i &- 1 to 10){
&&val thread:Thread = new Thread(new Runnable {
& & override def run(): Unit = {
& && &logs.foreachRDD(rdd =&{
& && &&&rdd.foreach(event =& {
& && && & // action-1
& && && & println(s&---------------${event}&)
& && && & Thread.sleep(100)
& && &&&})
&&thread.start()
你可以把rdd的treasaction和actoin操作,包装一下,成为一个Runnable,在runnable中去写每一个线程要执行的作业,当actin触发的时候,每一个线程的actin操作都将触发job的提交,形成多个job的提交,提交的多个job会根据你的调度策略进行作业的调度。
主题帖子积分
中级会员, 积分 429, 距离下一级还需 571 积分
中级会员, 积分 429, 距离下一级还需 571 积分
Spark Streaming可以用公平调度策略么?
比如某个时间窗口的数据处理耗时长,先达到了,但是因为耗时长,后来的时间窗口数据耗时短,还的等之前的处理完,才会处理短的。怎么让短的也立即被处理?
积极上进,爱好学习
经常帮助其他会员答疑
站长推荐 /4
云计算hadoop视频大全(新增 yarn、flume|storm、hadoop一套视频
等待验证会员请验证邮箱
新手获取积分方法
技术类问答,解决学习openstack,hadoop生态系统中遇到的问题
Powered byiOS并行开发:从NSOperation和调度队列开始
招聘信息:
本文由CocoaChina译者翻译作者:原文:在iOS开发中,并行一直被认为是项目里的怪物。它被认为是一个危险的区域,许多开发者尽力去避免的区域。有谣传说多线程代码应尽可能的避免。我同意并行是危险的,不过那只是因为你没有很好地理解。只是因为未知才变得危险。想想人们在生活中危险的行为活动,有很多吧?但是一旦掌握了,就变得简单了。并行是双刃剑,你应该学习并且掌握如何使用它。它帮助你编写高效、执行快速和响应灵敏的应用,但与此同时,滥用它会无情地毁了你的应用。因此在开始编写任何并行代码之前,先想想你为什么需要并行以及你需要使用哪个API来解决这个问题?在iOS中我们可以使用不同的API。本教程我们将讨论两个最常用的API——NSOperation和调度队列。我们为什么需要并行?我知道你是一个优秀的有经验的iOS开发者。然而,无论构建什么样的应用程序,你需要知道并行能使你的应用响应更灵敏、运行更快速。在这里我总结了几点学习或使用并行的优点:利用iOS设备的硬件:现在所有的iOS设备都有一个多核的处理器,它使开发人员可以并行执行多个任务。你应该利用这个特性,获得硬件带来的好处。更好的用户体验:你大概已经写过代码来调用web服务,处理一些IO,或执行任何繁重的任务。你知道,在UI线程执行这些操作将卡住应用,使其未响应。一旦用户面临这种情况,第一步,他/她将杀死/关闭应用,没有任何其它想法。有了并行,所有这些任务可以在后台完成,不会阻塞主线程,不会打扰用户。他们仍可以点击按钮、滚动和浏览你的应用,而在后台处理繁重的任务。NSOperation和调度队列之类的API使得并行容易使用:创建和管理线程并不是容易的任务。这就是为什么大部分开发者听到并行和多线程的代码会感到害怕的原因。在iOS中,我们有很棒的易用的并行API,它将使你的编程变得更容易。你不需要关心创建线程或管理任何底层的东西。API将为你完成所有这些任务。这些API的另一个重要优势是,它可以帮助你轻松实现同步来避免竞态条件。竞态条件发生在多个线程试图访问共享资源时,这会导致意想不到的结果。通过使用同步,能够保护资源免受线程间共享的影响。关于并行你需要了解什么?在本教程中,我们将解释所有关于并行你需要的了解的,并减轻你所有关于并行的恐惧。首先我们推荐看看block(在Swift中是闭包),因为大量并行API使用它。接着我们将谈谈调度队列和NSOperationQueues。我们将带你了解一下每个并行的概念,它们的不同点,以及如何实现它们。第1部分:GCD(伟大的中枢调度)GCD是最常用的管理并行代码和执行异步操作的Unix系统层的API。GCD构造和管理队列中的任务。首先,让我们看看队列是什么。队列是什么?队列是按先进先出(FIFO)管理对象的数据结构。队列类似电影院的售票窗口,票的销售是谁先到谁先服务。在等待线前面的人先去买他们的门票,在其余的后抵达的人之前。队列在计算机科学中是相似的,因为第一个添加到队列的对象也是第一个从队列中删除的对象。调度队列调度队列是一种简单的异步和同步任务的方法。它们是队列,任务以block的形式被你的应用提交到其中。有两种调度队列:(1)串行队列(2)并行队列。在谈不同点之前,你需要知道任务分配给这两个队列都是在单独的线程执行的,而不是在创建任务的线程上。换句话说,你创建block再提交到主线程的调度队列。但所有这些任务(block)将运行在单独的线程,而不是主线程。串行队列当你选择创建一个串行队列,队列一次只能执行一个任务。同一串行队列的所有任务将相互尊重并连续执行。然而,它们不关心任务是不是在单独的线程,这意味着你仍然可以通过使用多个串行队列来并行地执行任务。例如,你可以创建两个串行队列,每个队列一次只执行一个任务,不过多达两个任务仍可并行执行。串行队列用于管理共享资源是极好的。它保证了序列化访问共享资源,防止了竞态条件。想象一个售票亭,有一群人想买电影票,这里的展台的工作人员就是一个共享资源。如果员工需要为这些人在同一时间服务,这将是混乱的。为了处理这种情况,人们需要排队(串行队列),这样员工可以一次服务一个顾客。重申,这并不意味着电影院一次只能处理一个客户。如果多设置两个展位,它可以同时处理三个客户。这就是为什么说你仍然可以通过使用几个串行队列来并行执行多个任务。使用串行队列的优点是:1.保证序列化访问共享资源,避免竞态条件。2.任务的执行顺序是可预测的。当你提交任务到一个串行调度队列,它们将按插入的顺序执行。3.你可以创建任意数量的串行队列。并行队列顾名思义,并行队列可以并行执行多个任务。任务(block)按添加到队列的顺序开始,但它们的执行会同时发生,它们不会相互等待。并行队列保证任务开始的顺序,但你不知道执行的顺序、执行时间或某个时间点的正在执行的任务数。例如,你提交三个任务(任务#1,#2和#3)到并行队列。任务将并行执行并且按添加到队列的顺序开始。然而,执行时间和完成时间各不相同。即使任务#2和#3的开始需要一些时间,在任务#1之前它们都可以完成。任务的执行是由系统决定的。使用队列既然我们已经解释了串行和并行队列,是时候看看我们可以如何使用它们。默认情况下,系统为每个应用提供了一个串行队列和四个并行队列。主调度队列是全局可用的串行队列,它在应用的主线程执行任务。它是用来更新应用的UI和执行所有与UI视图更新有关的任务。同时只有一个任务执行,这就是为什么当你在主队列执行一个繁重的任务UI会被卡住。除了主队列,系统提供4个并行队列。我们称之为全局调度队列。这些队列对于应用是全局的,区别只在于它们的优先级。使用一个全局并行队列,你必须得到队列的引用,使用函数dispatch_get_global_queue,它的第一个参数是:DISPATCH_QUEUE_PRIORITY_HIGHDISPATCH_QUEUE_PRIORITY_DEFAULTDISPATCH_QUEUE_PRIORITY_LOWDISPATCH_QUEUE_PRIORITY_BACKGROUND这些队列类型代表了执行的优先级。HIGH的队列拥有最高的优先级,BACKGROUND拥有最低的优先级。所以你可以根据任务的优先级决定你使用的队列。也请注意,这些队列也被苹果API使用,所以你的任务并不是这些队列里唯一的任务。最后,你可以创建任意数量的串行或并行队列。关于并行队列,我强烈推荐使用的那四个全局队列,不过你也可以创建自己的。GCD备忘表现在,你应该对调度队列有了一个基本的了解。我要给你一个简单备忘表,供你参考。表非常简单,包含了关于GCD你需要知道的所有信息。很酷,对吧?现在让我们做一个简单的演示,来看看如何使用调度队列。我将向你展示如何利用调度队列来优化应用性能,使其响应更灵敏。演示项目我们的开始项目非常简单,我们显示四个image view,每一个从远程站点请求一个特定的形象。图像请求在主线程中完成,来向你们展示这将如何影响界面的响应,我在图片下面添加了一个简单的滑块。。单击Start按钮启动图像下载并在图像下载时拖动滑动条。你会发现你一点也拖不动。一旦你点击Start按钮,图像在主线程开始下载。显然,这种方法非常糟糕,它使UI没有响应。遗憾的是直到今天仍有一些应用在主线程执行繁重的加载任务。现在我们要修复它,使用调度队列。首先,我们将实现并行队列的解决方案,然后再用串行队列。使用并行调度队列现在回到Xcode项目里的ViewController.swift。如果你分析了代码,你应该看到didClickOnStart动作方法。该方法处理图像下载。我们现在是这样执行任务的:@IBAction&func&didClickOnStart(sender:&AnyObject)&{
&&&&let&img1&=&Downloader.downloadImageWithURL(imageURLs[0])
&&&&self.imageView1.image&=&img1
&&&&let&img2&=&Downloader.downloadImageWithURL(imageURLs[1])
&&&&self.imageView2.image&=&img2
&&&&let&img3&=&Downloader.downloadImageWithURL(imageURLs[2])
&&&&self.imageView3.image&=&img3
&&&&let&img4&=&Downloader.downloadImageWithURL(imageURLs[3])
&&&&self.imageView4.image&=&img4
}每个下载器被认为是一个任务,现在所有的任务都在主队列执行。现在让我们得到一个Default优先级的全局并行队列的引用。let&queue&=&dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,&0)
&&&&&&&&dispatch_async(queue)&{&()&->&Void&in
&&&&&&&&&&&&
&&&&&&&&&&&&let&img1&=&Downloader.downloadImageWithURL(imageURLs[0])
&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&self.imageView1.image&=&img1
&&&&&&&&&&&&})
&&&&&&&&&&&&
&&&&&&&&}我们首先使用dispatch_get_global_queue,得到一个默认并行队列的引用,然后在block里我们提交一个任务来下载第一个图像。一旦图像下载完成,我们提交另一个任务到主队列来用下载的图像更新image view。换句话说,我们把图片下载任务放到一个后台线程,但UI相关的任务在主队列执行。如果你其余图片也这样做,你的代码应该是这样的:@IBAction&func&didClickOnStart(sender:&AnyObject)&{
&&&&let&queue&=&dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,&0)
&&&&dispatch_async(queue)&{&()&->&Void&in
&&&&&&&&let&img1&=&Downloader.downloadImageWithURL(imageURLs[0])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView1.image&=&img1
&&&&&&&&})
&&&&dispatch_async(queue)&{&()&->&Void&in
&&&&&&&&let&img2&=&Downloader.downloadImageWithURL(imageURLs[1])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView2.image&=&img2
&&&&&&&&})
&&&&dispatch_async(queue)&{&()&->&Void&in
&&&&&&&&let&img3&=&Downloader.downloadImageWithURL(imageURLs[2])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView3.image&=&img3
&&&&&&&&})
&&&&dispatch_async(queue)&{&()&->&Void&in
&&&&&&&&let&img4&=&Downloader.downloadImageWithURL(imageURLs[3])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView4.image&=&img4
&&&&&&&&})
}你刚刚提交了四个图像下载的并行任务到默认队列。现在构建并运行应用,它应该快得多(如果报任何错误,检查你的代码是不是和上面的一样)。注意,你应该能够在下载图片的时候没有任何延迟地拖动滑块。使用串行调度队列解决滞后问题的备用方法是使用串行队列。现在回到ViewController.swift文件里的相同的didClickOnStart()方法。这次我们将使用一个串行队列来下载图片。在使用串行队列时,你需要密切关注你正在引用的串行队列是哪一个。每个应用都有一个默认的串行队列,这实际上是用于UI的主队列。所以记住当使用串行队列时,你必须创建一个新队列,否则会在应用试图执行更新UI的任务的时候执行你的任务。这将导致错误和延迟,破坏用户体验。你可以使用函数dispatch_queue_create来创建一个新队列,接着和之前做的一样提交所有任务。修改之后,代码是这样的:@IBAction&func&didClickOnStart(sender:&AnyObject)&{
&&&&let&serialQueue&=&dispatch_queue_create("com.appcoda.imagesQueue",&DISPATCH_QUEUE_SERIAL)
&&&&dispatch_async(serialQueue)&{&()&->&Void&in
&&&&&&&&let&img1&=&Downloader&.downloadImageWithURL(imageURLs[0])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView1.image&=&img1
&&&&&&&&})
&&&&dispatch_async(serialQueue)&{&()&->&Void&in
&&&&&&&&let&img2&=&Downloader.downloadImageWithURL(imageURLs[1])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView2.image&=&img2
&&&&&&&&})
&&&&dispatch_async(serialQueue)&{&()&->&Void&in
&&&&&&&&let&img3&=&Downloader.downloadImageWithURL(imageURLs[2])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView3.image&=&img3
&&&&&&&&})
&&&&dispatch_async(serialQueue)&{&()&->&Void&in
&&&&&&&&let&img4&=&Downloader.downloadImageWithURL(imageURLs[3])
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&{
&&&&&&&&&&&&
&&&&&&&&&&&&self.imageView4.image&=&img4
&&&&&&&&})
}正如我们看到的,与并行队列唯一不同的地方是串行队列的创建。当构建并再次运行应用时,你将再次看到图片在后台下载,所以你可以继续与用户界面交互。但你会注意到两件事:1.和并行队列相比,下载图片需要的时间有点长。这是因为我们同时只加载一个图像。每个任务等待前面的任务完成才会被执行。2.图像按image1,image2,image3,image4的顺序加载。因为队列是一个串行队列,它一次执行一个任务。第2部分:操作队列GCD是一个底层的C的API,它使开发人员能够并行地执行任务。操作队列,另一方面,是高度抽象的队列模型,是建立在GCD之上的。这意味着你可以并行执行任务就像GCD一样,但以面向对象的方式。简而言之,队列操作让编程更加简单。不同于GCD,它们不按先进先出的顺序。下面是操作队列和调度队列的不同点:1.不遵循先进先出:在操作队列中,你可以设置一个操作的执行优先级,你可以添加操作之间的依赖关系,这意味着你可以定义一些操作完成后才会执行其他操作。这就是为什么它们不遵循先进先出。2.默认情况下,它们同时操作:然而你不能把它的类型改变成串行队列。通过使用操作之间的依赖关系,在操作队列还存在一个工作区来依次执行任务。3.操作队列是类NSOperationQueue的实例,其任务封装在NSOperation的实例里。NSOperation任务以NSOperation实例的形式提交到操作队列。我们在GCD讨论了任务是以block提交。同样这里也可以这样做但应捆绑在NSOperation实例里。你可以简单地认为NSOperation是单个的工作单元。NSOperation是一个抽象类,它不能直接使用,所以你必须使用NSOperation子类。在iOS SDK里,我们提供两个NSOperation的具体子类。这些类可以直接使用,但你也可以继承NSOperation来创建自己的类来执行操作。我们可以直接使用的两个类:1.NSBlockOperation——使用这个类来用一个或多个block初始化操作。操作本身可以包含多个块。当所有block被执行操作将被视为完成。2.NSInvocationOperation——使用这个类来初始化一个操作,它包括指定对象的调用selector。所以NSOperation的优势是什么?1.首先,它们通过NSOperation类里的方法addDependency(op:NSOperation)支持依赖。当你需要开始一个依赖于其它操作执行的操作,你会需要NSOperation。2.其次,你可以通过下面这些值设置属性queuePriority来改变执行优先级:public&enum&NSOperationQueuePriority&:&Int&{
&&&&case&VeryLow
&&&&case&Low
&&&&case&Normal
&&&&case&High
&&&&case&VeryHigh
}优先级高的操作将先被执行。3.对于任何给定的队列,你可以取消一个特定的或所有的操作。操作可以在被添加到队列后被取消。取消是通过调用NSOperation类里的方法cancel()。当你取消任何操作的时候,我们有三个场景,其中一个会发生:你的操作已经完成。在这种情况下,取消方法没有效果。你的操作已经被执行。在这种情况下,系统不会强制操作代码停止,而是属性cancelled被置为true。你的操作仍在队列中等待。在这种情况下,你的操作将不会被执行。4.NSOperation有3个有用的布尔属性,finished、 cancelled和ready。一旦操作执行完成,finisher将被置为true。一旦操作被取消,cancelled将被置为true。一旦准备即将被执行,ready将被置为true。5.任何NSOperation有一个选项来设置回调,一旦任务完成将会被调用。在NSOperation里,一旦属性finished被置为true,这个block将被调用。现在让我们重写演示项目,但这一次我们将使用NSOperationQueues。首先在ViewController类里声明变量:var&queue&=&NSOperationQueue()接下来,用下面的代码替换didClickOnStart方法,看看我们在NSOperationQueue里如何执行操作:@IBAction&func&didClickOnStart(sender:&AnyObject)&{
&&&&queue&=&NSOperationQueue()
&&&&queue.addOperationWithBlock&{&()&->&Void&in
&&&&&&&&let&img1&=&Downloader.downloadImageWithURL(imageURLs[0])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView1.image&=&img1
&&&&&&&&})
&&&&queue.addOperationWithBlock&{&()&->&Void&in
&&&&&&&&let&img2&=&Downloader.downloadImageWithURL(imageURLs[1])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView2.image&=&img2
&&&&&&&&})
&&&&queue.addOperationWithBlock&{&()&->&Void&in
&&&&&&&&let&img3&=&Downloader.downloadImageWithURL(imageURLs[2])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView3.image&=&img3
&&&&&&&&})
&&&&queue.addOperationWithBlock&{&()&->&Void&in
&&&&&&&&let&img4&=&Downloader.downloadImageWithURL(imageURLs[3])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView4.image&=&img4
&&&&&&&&})
}正如你在上面的代码中所看到的,你使用方法addOperationWithBlock用给定的block(或Swift中的闭包)来创建一个新的操作。这很简单,不是吗?要在主队列执行一项任务,不用调用使用GCD时用的dispatch_async(),我们可以从NSOperationQueue(NSOperationQueue.mainQueue())提交你想要执行的操作到主队列。你可以运行这个应用来做一个快速测试。如果输入的代码是正确的,应用应该能够在后台下载图片并且不会阻塞用户界面。在前面的示例中,我们使用的方法addOperationWithBlock在队列中添加操作。让我们看看如何使用NSBlockOperation做相同的事,而且与此同时,它为我们提供更多功能和选项,如设置回调。didClickOnStart方法修改后是这样的:@IBAction&func&didClickOnStart(sender:&AnyObject)&{
&&&&queue&=&NSOperationQueue()
&&&&let&operation1&=&NSBlockOperation(block:&{
&&&&&&&&let&img1&=&Downloader.downloadImageWithURL(imageURLs[0])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView1.image&=&img1
&&&&&&&&})
&&&&pletionBlock&=&{
&&&&&&&&print("Operation&1&completed")
&&&&queue.addOperation(operation1)
&&&&let&operation2&=&NSBlockOperation(block:&{
&&&&&&&&let&img2&=&Downloader.downloadImageWithURL(imageURLs[1])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView2.image&=&img2
&&&&&&&&})
&&&&pletionBlock&=&{
&&&&&&&&print("Operation&2&completed")
&&&&queue.addOperation(operation2)
&&&&let&operation3&=&NSBlockOperation(block:&{
&&&&&&&&let&img3&=&Downloader.downloadImageWithURL(imageURLs[2])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView3.image&=&img3
&&&&&&&&})
&&&&pletionBlock&=&{
&&&&&&&&print("Operation&3&completed")
&&&&queue.addOperation(operation3)
&&&&let&operation4&=&NSBlockOperation(block:&{
&&&&&&&&let&img4&=&Downloader.downloadImageWithURL(imageURLs[3])
&&&&&&&&NSOperationQueue.mainQueue().addOperationWithBlock({
&&&&&&&&&&&&self.imageView4.image&=&img4
&&&&&&&&})
&&&&pletionBlock&=&{
&&&&&&&&print("Operation&4&completed")
&&&&queue.addOperation(operation4)
}对于每个操作,我们创建一个新的NSBlockOperation的实例来将任务封装到一个block。通过使用NSBlockOperation,你得以设置回调。现在当操作完成,回调将被调用。为简单起见,我们只输出一个简单消息来表示操作完成。如果你运行这个演示,你将在控制台看到这样的东西:Operation&1&completed
Operation&3&completed
Operation&2&completed
Operation&4&completed取消操作如前所述,NSBlockOperation允许你管理操作。现在让我们看看如何取消操作。要做到这一点,首先添加一个按钮到导航栏并把它命名Cancel。为了说明取消操作,我们将添加操作#2和操作#1之间的依赖,以及操作#3和操作#2之间的依赖。这意味着操作#2将在操作#1完成后开始,操作#3将在操作#2完成后开始。操作#4没有依赖,它将并行工作。为了取消操作,你所有需要做的就是调用NSOperationQueue的cancelAllOperations()。在ViewController类中插入下面的方法:@IBAction&func&didClickOnCancel(sender:&AnyObject)&{
&&&&&&&&self.queue.cancelAllOperations()
&&&&}记住你需要把你添加到导航栏的Cancel按钮关联到didClickOnCancel方法。为此你可以回到Main.storyboard文件打开Connections Inspector。在那里你会在Received Actions部分看到取消关联didSelectCancel()。单击+拖动从空圆到Cancel工具栏按钮。然后在didClickOnStart方法创建依赖是这样的:operation2.addDependency(operation1)
operation3.addDependency(operation2)接着改变操作#1的回调来输出取消状态:pletionBlock&=&{
&&&&&&&&&&&&print("Operation&1&completed,&cancelled:\(operation1.cancelled)&")
&&&&&&&&}你可以改变操作#2、#3和#4的日志语句,这样你将更好地理解这个过程。现在让我们构建并运行。点击Start按钮后,按下Cancel按钮。这将在操作#1完成后取消所有操作。发生了以下的事:由于操作#1已经执行,取消将什么也不做。这就是为什么cancelled值输出为false,而且应用仍然显示图像#1。如果你点击Cancel按钮的速度足够快,操作#2被取消了。cancelAllOperations()将停止执行,所以图像#2没有被下载。操作#3已经在队列中,等待操作#2完成。因为它依赖于操作#2的完成然而操作#2被取消,操作#3将不会被执行并立即从队列中被剔除。没有依赖配置到操作#4。它就并发下载图片#4。接下来呢?在本教程中,我带你了解了iOS并发的概念,以及如何在iOS实现它。我给了你一个很好的并发的介绍,解释了GCD,展示了如何创建串行和并发队列。此外,我们还看了NSOperationQueues。你现在应该熟悉GCD和NSOperationQueue之间的区别。要进一步深入iOS并发,我建议你看看。为了参考,你可以。请随意问任何问题。我喜欢阅读你的评论。更多译者翻译文章,请查看:本文仅用于学习和交流目的,转载请注明文章译者、出处和本文链接。&感谢对本期活动的支持
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量6265点击量4590点击量4565点击量3500点击量2909点击量2409点击量2381点击量2351点击量2333
&2016 Chukong Technologies,Inc.
京公网安备89

我要回帖

更多关于 gtest 并行执行 串行 的文章

 

随机推荐