-
Task.CompletedTask和Task.Result小记
2019-12-20 07:15:00在任何返回Task的方法中,如果可以在不进行异步的情况下计算结果,则最好避免使用Task.Run。例如,一个简短的计算函数,或者测试中返回了一个预先计算过的结果,则无需使用Task.Ru...在任何返回Task的方法中,如果可以在不进行异步的情况下计算结果,则最好避免使用Task.Run。例如,一个简短的计算函数,或者测试中返回了一个预先计算过的结果,则无需使用Task.Run。
例如,定义了一个返回Task的接口方法,但是在实现中,并没有特别耗时的代码。
public interface IComputer { Task Do(); Task<string> DoString(); }
一般我们会这样实现。即使实现中代码很简短。
public class Computer : IComputer { public Task Do() { return Task.Run(() => { //逻辑代码 }); } public Task<string> DoString() { return Task.Run( () => { //逻辑代码 return "aaa"; }); } }
实际上,Task类上面有两个帮助程序,,这些帮助程序让代码更具可读性,并且所需的运行时开销更少。
对于上述接口实现中的 Do方法,我更愿意使用Task.CompletedTask。
public Task Do() { //逻辑代码 return Task.CompletedTask; }
我们看一下CompletedTask的定义
/// <summary>Gets a task that has already completed successfully.</summary> /// <returns>The successfully completed task.</returns> public static Task CompletedTask { get; }
如果返回Task <T>怎么办?在这种情况下,如果您已经有了答案,请使用Task.FromResult包装答案。
public Task<string> DoString() { //逻辑代码 return Task.FromResult("aaa"); }
看一下FromResult的定义
/// <summary>Creates a <see cref="T:System.Threading.Tasks.Task`1" /> that's completed successfully with the specified result.</summary> /// <param name="result">The result to store into the completed task.</param> /// <typeparam name="TResult">The type of the result returned by the task.</typeparam> /// <returns>The successfully completed task.</returns> public static Task<TResult> FromResult<[Nullable(2)] TResult>(TResult result);
通过上述的改变,即实现了接口的实现,也减少了Task.Run调用的系统开销。
·END·
-
Task详解
2019-12-23 10:48:571,Task 的创建 //直接通过 task 函数创建 task("helloTask") { println '--------- i am helloTask' } //通过 TaskContainer 去创建 Task this.tasks.create(name: "helloTask2") { println '--------- i am ...1,Task 的创建
//直接通过 task 函数创建 task("helloTask") { println '--------- i am helloTask' } //通过 TaskContainer 去创建 Task this.tasks.create(name: "helloTask2") { println '--------- i am helloTask2' }
以上两种方式创建的 Task 没有任何区别,
this.tasks 返回的就是 TaskContainer 类,Task 的容器类,上面两种方式创建的 Task 都会被添加当前 project 的 TaskContainer 类中,TaskContainer 是用来管理当前 project 中所有的 Task。既然是用来管理的,他肯定可以对 Taks 进行一些操作,如下:
public interface TaskContainer extends TaskCollection<Task>, PolymorphicDomainObjectContainer<Task> { //在指定路径下查找 task ,没有返回 null @Nullable Task findByPath(String path); //同上,没有抛出异常 Task getByPath(String path) throws UnknownTaskException; //非常多的创建方法 Task create(Map<String, ?> options) throws InvalidUserDataException; Task create(Map<String, ?> options, Closure configureClosure) throws InvalidUserDataException; Task create(String name, Closure configureClosure) throws InvalidUserDataException; Task create(String name) throws InvalidUserDataException; }
无论使用那种方式创建 Task ,最终 TaskContainer 会将所有的 Task 在配置阶段构成一个有向无环图。通过这个图 gradle 就能知道 task 的类别和执行顺序。
2,Task配置
可以在创建的时候直接配置组合描述信息,如下:
task helloTask(group: '345', description: 'Task Study') { println '--------- i am helloTask' }
也可以在闭包中进行设置描述信息:
this.tasks.create(name: "helloTask2") { setGroup("345") setDescription("Task Study") println '--------- i am helloTask2' }
第一种看起来比较简洁,指定 group 后 ,就会将 task 放在对应的组中。description 就相当于是注释。指定分组后,点击 as 右上角的 Gradle ,打开指定 task 的 module。就会显示当前 project 的所有 task。如果指定了分组,则就会有对应的文件夹,如果没有就会放在 other 中,如下:
当然 task 不会只能配置上面两种信息。打开 Task 类,如下:
public interface Task extends Comparable<Task>, ExtensionAware { //名字 String TASK_NAME = "name"; //描述 String TASK_DESCRIPTION = "description"; //组 String TASK_GROUP = "group"; //类型 String TASK_TYPE = "type"; //指定当前 task 依赖于那些 task String TASK_DEPENDS_ON = "dependsOn"; //重写 task String TASK_OVERWRITE = "overwrite"; //配置要执行的逻辑 String TASK_ACTION = "action"; }
3,让 Task 执行在 执行阶段
首先我们有运行上面的例子 gradlew helloTask ,如下:
--------- i am helloTask --------- i am helloTask2
这两个都执行了。为啥呢?他们是在 配置阶段执行的,并不是执行阶段,所以他们都会被执行。下面我们就看一下执行阶段。
执行阶段是 gradle 生命周期的第三阶段。也只有 Task 才能在第三阶段中执行,要将 Task 执行在 执行阶段,有两个方法,分别是 doFirst 和 doLast ,通过 doFirst 可以为已经有的 task 之前添加相应的逻辑,doLast 则是之后了,下面看一下简单的使用:
task helloTask(group: '345', description: 'Task Study') { // doFirst 执行在 执行阶段 doFirst { println '--------- i am helloTask --1' } //可执行多次 doFirst { println '--------- i am helloTask --2' } doLast{ println "last" } } helloTask.doFirst { //可定义在外面 println '--------- i am helloTask --3' }
打印结果:
> Task :app:helloTask --------- i am helloTask --3 --------- i am helloTask --2 --------- i am helloTask --1 last
在配置阶段执行的时候是不会打印 Task :app:helloTask 这句话的。这里的执行顺序是从外部的开始执行,最后执行的是 doLast 。
小例子:计算 Build 中 task 的执行时长
// 计算 build 计算时长 def startBuildTime, endBuildTime this.afterEvaluate { Project p -> def preBuildTask = p.tasks.getByName('preBuild') preBuildTask.doFirst { startBuildTime = System.currentTimeMillis() println "this start time is :" + startBuildTime } def buildTask = p.tasks.getByName('build') buildTask.doLast { endBuildTime = System.currentTimeMillis() println "this build time is :${endBuildTime - startBuildTime}" } }
使用命令 gradlew build 开始 build
执行结果:
> Task :app:preBuild this start time is :1576560417687 > Task :app:build this build time is :28783
其中 this.afterEvaluate 是生命周期中的方法,绘制配置阶段执行所有的 task 配置完成后执行,接着就是获取 preBuild task,他是第一个开始执行的 task,不信你可以看一下控制台的log。在 preBuild 的 doFirst 中获取开始时间,在 build task 完成后获取结束时间即可。
4,Task 执行顺序
看上面的小例子:我们执行的命令是 gradlew build ,那为啥先执行 preBuild 呢!这就和 Task 的执行顺序有关了。
-
强依赖方式
为 task 指定多个依赖性的 task。这样当前的 task 必须等依赖的 task 执行完才可以执行
task TaskX { doLast { println 'taskX' } } task TaskY { doLast { println 'taskY' } } //依赖 taskX ,taskY task TaskZ(dependsOn: [TaskX, TaskY]) { doLast { println 'taskZ' } } // TaskZ.denpendsOn(TaskX, TaskY) 这种也可以
执行结果:
> Task :app:TaskX taskX > Task :app:TaskY taskY > Task :app:TaskZ taskZ
如果在最开始不知道要依赖那些 task,可以通过下面这种方式:
task TaskX { doLast { println 'taskX' } } task TaskY { doLast { println 'taskY' } } //依赖 taskX ,taskY task Test { dependsOn this.tasks.findAll { task -> //测试此字符串是否以指定的前缀开始。 return task.name.startsWith('Task') } doLast { println 'Test' } }
结果如下:
> Task :app:TaskX taskX > Task :app:TaskY taskY > Task :app:Test Test
-
通过 api 来指定顺序
//执行顺序指定 task taskX { doLast { println 'taskX' } } task taskY { //指定执行在 TaskX 之后 mustRunAfter taskX doLast { println 'taskY' } } task taskZ { //指定执行在 TaskY 之后 mustRunAfter taskY doLast { println 'taskZ' } }
输入命令:gradlew taskX taskZ TASKZ 结果如下:
> Task :app:taskX taskX > Task :app:taskY taskY > Task :app:taskZ taskZ
最后看一个例子:
ext { versionName = '1.0.1' versionCode = 1 versionInfo = '第1个版本' destFile = file('releases.json') if (destFile != null && !destFile.exists()) { destFile.createNewFile() } }
上面的 ext 不会陌生吧,他是扩展属性。内部定义了一写属性,创建了一个文件 releases.json
接着定义一个类,来保存这些属性:
class VersionMsg { String versionName String versionCode String versionInfo }
然后创建一个 Task。用来给文件中写入信息:
task writeTask { //为 task 指定输入 inputs.property('versionName', this.versionName) inputs.property('versionCode', this.versionCode) inputs.property('versionInfo', this.versionInfo) //为 task 指定输出 outputs.file this.destFile doLast { //获取输入让的信息,返回 map def map = inputs.getProperties() File file = outputs.getFiles().getSingleFile() def versionMsg = new VersionMsg(map) def json = JsonOutput.toJson(versionMsg) if (file.text != null && file.text.size() >= 0) { def lines = file.readLines() def lengths = lines.size() //一共多少行 file.withWriter { writer -> if (lengths == 0) { writer.append("[") //将json串格式化后写入 writer.append("${JsonOutput.prettyPrint(json)}\n") } else { for (int i = 0; i < lines.size(); i++) { if (i == lengths - 1) { writer.append(",") break } writer.append(lines[i] + "\n") } writer.append("${JsonOutput.prettyPrint(json)}\n") } writer.append("]") } } } }
inputs 和 outputs 是 Task 的属性,inputs 可以是任意类型的对象,而 outputs只能是文件(或文件夹)
上面这段代码首先是指定了输入和输出,然后获取输入的文件,并传入到 VersionMsg类中,接着将这个类转为 json 串。最后将 json 写入到文件中。
注意要使用 JsonOutput ,必须先导包,否则不能使用,如下:
import groovy.json.JsonOutput
最终执行 gradlew writeTask 命令,执行前请修改信息,写入的文件如下:
这就是输出的版本信息,其中 contentHash 和 originalClassName 是自动生成的。
在这个过程中也遇到了一个问题:在执行 Task 的时候,如果 Task 内涉及的内容没有发生任何变化,那么这个 task 不会执行。如果上面的执行完第三次后没有修改任何地方,在继续执行 Task 则不会执行。但是只要有任何修改,task 都会执行:如将 文件中的 json 传格式化一下,修改一下等,再次执行命令,则 task 就会执行。
到这里就完了,但是还是有些疑惑,在学习的过程中,看到可以使用 输入输出来指定执行的顺序,但是我却没弄出来。还有就是指定在 build Task 后执行,但是定义的 task 没有 execute() 方法,一直报错,无法在代码中使其运行。
这就是输出的版本信息,其中 contentHash 和 originalClassName 是自动生成的。
在这个过程中也遇到了一个问题:在执行 Task 的时候,如果 Task 内涉及的内容没有发生任何变化,那么这个 task 不会执行。如果上面的执行完第三次后没有修改任何地方,在继续执行 Task 则不会执行。但是只要有任何修改,task 都会执行:如将 文件中的 json 传格式化一下,修改一下等,再次执行命令,则 task 就会执行。
到这里就完了,但是还是有些疑惑,在学习的过程中,看到可以使用 输入输出来指定执行的顺序,但是我却没弄出来。还有就是指定在 build Task 后执行,但是定义的 task 没有 execute() 方法,一直报错,无法在代码中使其运行。
希望有解决过的同学讲一哈,多谢
-
-
C# Task.Run 和 Task.Factory.StartNew 区别
2019-01-29 16:14:39有小伙伴问我,为什么不推荐他使用 Task.Factory.StartNew ,因为 Task.Run 是比较新的方法。 本文告诉大家 Task.Run 和 Task.Factory.StartNew 区别有小伙伴问我,为什么不推荐他使用 Task.Factory.StartNew ,因为 Task.Run 是比较新的方法。
本文告诉大家 Task.Run 和 Task.Factory.StartNew 区别
有很多博客说到了 Task.Run 和 Task.Factory.StartNew 区别,所以我也就不需要展开告诉大家。
只需要知道
Task.Run
是在 dotnet framework 4.5 之后才可以使用,但是Task.Factory.StartNew
可以使用比Task.Run
更多的参数,可以做到更多的定制。可以认为
Task.Run
是简化的Task.Factory.StartNew
的使用,除了需要指定一个线程是长时间占用的,否则就使用Task.Run
创建新线程
下面来告诉大家使用两个函数创建新的线程
Task.Run(() => { var foo = 2; });
这时 foo 的创建就在另一个线程,需要知道 Task.Run 用的是线程池,也就是不是调用这个函数就会一定创建一个新的线程,但是会在另一个线程运行。
Task.Factory.StartNew(() => { var foo = 2; });
可以看到,两个方法实际上是没有差别,但是
Task.Run
比较好看,所以推荐使用Task.Run
。等待线程
创建的线程,如果需要等待线程执行完成在继续,那么可以使用 await 等待
private static async void SeenereKousa() { Console.WriteLine("开始 线程"+Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Console.WriteLine("进入 线程" + Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine("退出 线程"+Thread.CurrentThread.ManagedThreadId); }
但是需要说的是这里使用 await 主要是给函数调用的外面使用,上面代码在函数里面使用 await 函数是 void 那么和把代码放在 task 里面是相同
private static async void SeenereKousa() { Console.WriteLine("开始 线程"+Thread.CurrentThread.ManagedThreadId); await Task.Run(() => { Console.WriteLine("进入 线程" + Thread.CurrentThread.ManagedThreadId); Console.WriteLine("退出 线程"+Thread.CurrentThread.ManagedThreadId); }); }
但是如果把 void 修改为 Task ,那么等待线程才有用
除了使用 await 等待,还可以使用 WaitAll 等待
Console.WriteLine("开始 线程" + Thread.CurrentThread.ManagedThreadId); var t = Task.Run(() => { Console.WriteLine("进入 线程" + Thread.CurrentThread.ManagedThreadId); }); Task.WaitAll(t); Console.WriteLine("退出 线程" + Thread.CurrentThread.ManagedThreadId);
使用 WaitAll 是在调用 WaitAll 的线程等待,也就是先在线程 1 运行,然后异步到 线程2 运行,这时线程1 等待线程2运行完成再继续,所以输出
开始 线程1 进入 线程2 退出 线程1
长时间运行
两个函数最大的不同在于
Task.Factory.StartNew
可以设置线程是长时间运行,这时线程池就不会等待这个线程回收Task.Factory.StartNew(() => { for (int i = 0; i < 100; i++) { var foo = 2; } Console.WriteLine("进行 线程" + Thread.CurrentThread.ManagedThreadId); }, TaskCreationOptions.LongRunning);
所以在需要设置线程是长时间运行的才需要使用
Task.Factory.StartNew
不然就使用Task.Run
调用
Task.Run(foo)
就和使用下面代码一样Task.Factory.StartNew(foo, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
实际上
Task.Run(foo)
可以认为是对Task.Factory.StartNew
封装,使用简单的默认的参数。如果需要自己定义很多参数,就请使用Task.Factory.StartNew
定义参数。我搭建了自己的博客 https://lindexi.gitee.io/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新
如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。 -
Android Gradle Task详解
2019-04-16 21:06:57本文介绍如何创建一个task,如何为自定义和现有的task的添加action。以及如何配置task之间的依赖。创建一个task
创建一个task很简单,直接使用task关键字来创建,最常见的形式是使用task + 名字 + Closure代码块。示例如下。
task ms1 { println("=================task ms1 added=================") }
也可以只有task + 名字,就像下面这样。当然这样的task什么也不做。
task myTask
创建task经常还会看到另外一种写法,在名字后面加上 << 符号。
task hello << { println 'Hello world!' }
这种写法和下面的写法是等同的,也就是在创建task的时候为其添加了一个action到末尾。不过这种写法目前已被标记为deprecated,预期会在Gradle 5.0版本中移除,所以最好不要再使用了。
task hello { doLast { println 'Hello world!' } }
上述几种写法都有一个共同点,就是创建task的时候必须指定一个名字,每个task都需要有一个名字,所有的task在创建之后都隶属于某个Project。同一个Project中任意两个task的名字不能相同。
此外,添加task不一定要在build.gradle的最外层,可以放在任意的代码块中,只是在执行时机上有所区别。
如下代码将task hello放到了allprojects()中执行,它会为项目中每一个project创建一个名为hello的task。
allprojects { task hello }
如下代码则在循环中创建了task0,task1,task2,task3四个不同的task。
4.times { index -> task "task$index" { println "I'm task number $index" } }
创建task的时候还可以指定一些参数。比较重要的有:
- type: task类型
- dependsOn:依赖的task列表
- group:task所属的组
为task添加Action
每个task中可以包含0到多个Action,这些Action保存在一个ArrayList成员变量中,当执行这个task时,会按照顺序依次执行所有的Action。
要为task添加Action,可以通过task的doFirst()和doLast()方法来实现。doFirst()方法会将Action添加到ArrayList的开头,而doLast()方法则将Action添加到ArrayList的末尾。可以有多个doFirst()和doLast(),配置task时会按照它们出现的先后顺序添加到Action列表中。
创建task的同时添加Action
可以在创建task的同时添加Action,只需要将其添加到创建task的闭包中即可。
以下代码在创建ms1 task的时候为它添加了4个action。
task ms1 { doFirst { println("=================ms1 doFirst1=================") } doFirst { println("=================ms1 doFirst2=================") } doLast { println("=================ms1 doLast1=================") } doLast { println("=================ms1 doLast2=================") } } -- 执行这个task会产生如下输出 -- =================ms1 doFirst2================= =================ms1 doFirst1================= =================ms1 doLast1================= =================ms1 doLast2=================
为现有task增加Action
也可以为已有的task增加Action。
如下代码为build task增加了两个Action,如果其他没有对build task再做修改,那么这里添加的doFirst会作为build task的第一个Action最先被执行,而doLast则会作为build task的最后一个Action,在其他Action执行完成后再执行。
build { doFirst { println("=================build doFirst=================") } doLast { println("=================build doLast=================") } }
如下代码为ms1 task添加了一个新的action,这个action被添加在了其他action的前面。
ms1.doFirst { println("=================ms1 doFirst=================") }
为动态创建的task增加Action
有些task是动态创建的,无法直接在build.gradle的最外层为其增加Action,又或者需要在task定义代码之前为其添加Action,这时可以将其放到Project.afterEvaluate()方法中去执行。
afterEvaluate { assembleDebug { doLast { println("=================assembleDebug doLast=================") } } }
或者可以在Task creation事件的回调中去添加。
tasks.whenTaskAdded { task -> if (task.name == 'assembleDebug') { task.doFirst { println("=====================assembleDebug doFirst=========================") } } }
从上面的三小节可以看出,添加action可以出现在代码任意地方,只要拿到对应的task就可以为其添加action。需要注意的是不能为正在运行的task添加action,否则会抛出异常。例如如下代码为myTask添加了一个action,在这个action执行的时候再为myTask添加另一个action,这时就会有运行时异常,但可以在一个task执行的时候为其他task添加action,这里将myTask改成任意其他的task名字是可以正常运行的。
myTask.doFirst { println("=================ms1 doFirst=================") myTask.doFirst { println("=================add another action=================") } }
指定Action名字
从Gradle 4.2版本开始支持在调用doFirst和doLast的时候为Action指定一个名字,不过这个特性仍然是Incubating状态,也就是试验阶段。关于Incubating状态的说明参见 https://docs.gradle.org/current/userguide/feature_lifecycle.html#sec:states 。
关于actionName这个参数是这样描述的。
actionName - An arbitrary string that is used for logging.
也就是说这个名字应当只用在打印log的时候。
添加task之间的依赖
task提供了dependsOn,finalizedBy方法来管理task之间的依赖关系,依赖关系表达的是执行这个task时所需要依赖的其他task,也就是说这个task不能被单独执行,执行这个task之前或之后需要执行另外的task。
在一个task前执行另一个task
要在一个task之前执行另一个task,可以通过配置dependsOn的依赖关系实现。
例如有两个task:taskA和taskB,通过指定taskA.dependsOn taskB就可以让在执行taskA之前先执行taskB,或者指定taskB.dependsOn taskA就可以让在执行taskB之前先执行taskA。
在一个task后执行另一个task
要在一个task之后执行另一个task,无法通过配置dependsOn实现。要实现这个功能需要通过finalizedBy来实现。
同样的有两个task:taskA和taskB,通过指定taskA.finalizedBy taskB就可以让在执行taskA之后执行taskB,或者指定taskB.finalizedBy taskA就可以让在执行taskB之后先执行taskA。
在一个task中执行另一个task
gradle在执行一组task的时候会根据他们的依赖关系生成一个task序列,然后按照序列的先后顺序来依次执行各个task。单个task的执行一定是一个原子过程,gradle不允许在一个task中执行另一个task,因此不会出现嵌套执行的情况。也就是说所有的task执行都是由gradle自身去调度,只有执行完一个task之后才会去执行下一个task,不会出现task没有执行完就转去执行另一个task中的任务的情况。
值得一提的是,现有gradle版本中为task保留了一个execute()方法来执行一个task,这个方法已被标记为deprecated,不过还是可以用的。
如下代码看起来是在ms2 task的最后一个action中去执行ms1 task,但实际上在执行gradlew ms2的时候,仍然会先执行ms1,然后再执行ms2,并不会出现执行ms2到最后一个action的时候,再转去执行ms1。所以它和添加ms2到ms1的依赖没有区别,鉴于这种用法已被标记为deprecated,且过程难以被理解,所以最好就不要用execute()了。
task ms2 { doLast { println("=====================ms2.=========================") ms1.execute() } } task ms1 { doLast { println("=====================ms1.=========================") } }
tasks队列
在通过dependsOn和finalizedBy设定好task之间的依赖关系后,在执行一个task时就会根据依赖关系生成一个task队列。
例如,有四个task,task0,task1,task2,task3。指定依赖关系如下。
task0 dependsOn task1
task1 dependsOn task2
task0 finalizedBy task3当执行gradlew task0时就会先生成 task2 -> task1 -> task0 -> task3这样一个序列,然后按照顺序来执行。
给tasks排序
在上述例子中,执行task0得到的tasks队列中每个task的位置都是明确的,它们的先后顺序也是完全确定的。然而更多的情况是,一个任务队列中有部分task的位置不是很明确。
例如,有四个task,task0,task1,task2,task3。指定依赖关系如下。
task0 dependsOn task1
task0 dependsOn task2
task0 finalizedBy task3按照依赖关系,执行task0生成的任务队列可以是task2 -> task1 -> task0 -> task3,也可以是task1 -> task2 -> task0 -> task3,可以看到task2和task1之间的顺序是不确定的。虽然gradle总是可以按照一定的规则(例如task名字的字典序)来得到一个固定的task队列,但有时我们需要人为明确这个顺序,例如这里我们希望task2总是在task1之前执行。这时有两种方案,一种是使用dependsOn,我们让task1 dependsOn task2,这样task2一定会在task1之前执行,但这种方案会让单独执行task1的时候也会先执行task2,这有可能和实际需要不符。这时就需要使用另一种方案,通过shouldRunAfter和mustRunAfter来指定这个顺序。
shouldRunAfter和mustRunAfter表达的都是一个任务需要在另一个任务之后执行,它们的区别是mustRunAfter要求gradle生成任务队列时必须确保这个顺序一定要满足,如果不能满足就会报错,shouldRunAfter相当于建议gradle按照这顺序来生成任务队列,gradle会优先参考其他约束条件,如果在满足其他约束条件后,这条约束也能满足,那么就采纳这个顺序,如果不能满足,就忽略这个请求。
例如有依赖关系task0 dependsOn task1,这时又指定task1 mustRunAfter task0,显然这时这个条件无法被满足,因此执行task0的时候会报错。但如果指定task1 shouldRunAfter task0,则不会有错误,gradle会自动忽略掉这条请求。
再来看上述例子
task0 dependsOn task1
task0 dependsOn task2
task0 finalizedBy task3这时无论指定task1 shouldRunAfter task2,还是task1 mustRunAfter task2,都会得到任务队列task2 -> task1 -> task0 -> task3。
需要注意的是,无论是shouldRunAfter还是mustRunAfter,影响的只是task在队列中的顺序,并不影响任何任务间的执行依赖,也就是说使用shouldRunAfter和mustRunAfter并不会导致任务队列中添加新的task。
例如指定ms1 mustRunAfter ms2,如果ms1和ms2没有其他依赖关系,那么在执行ms1的时候并不会先执行ms2,执行ms2之后也不会执行ms1。
预定义的gradle task
gradle为每个工程提供了很多预先定义好的task,通过这些task我们可以不需要手动创建任何task就可以实现编译,打包,测试等功能。在前文中已经出现的clean build等就是gradle中预定义的task。在做Android开发时,Android Gradle插件也提供了一组预定义的gradle task,当我们新建一个Android工程就能看到如下所示的task列表,这里的tasks都是gradle和Android Gradle Plugin为我们创建好的,可以直接使用。
清理工程(clean task)
clean task用来清理上次编译的缓存内容
编译相关task
和编译相关的task主要有:build和assemble,其中build依赖assemble,也就是说执行build之前会先执行assemble。在Android上,会根据buildType和productFlavor的不同自动创建多个assembleXxx任务,如assembleDebug,assembleRelease等,assemble会依赖所有的assembleXxx任务,也就是说执行assemble会先执行assembleDebug,assembleRelease等一系列的assemble任务。
在编译完成后执行一段代码
有时我们需要在编译任务完成后执行某一个任务,按照通常的做法,我们创建一个task,然后将这个task放到编译的task之后执行。
如前所述要在一个task之后执行另一个task,可以通过finalizedBy来实现,但是编译任务对应的task很多,有些还是动态创建的,如果每个任务都指定一遍finalizedBy会很麻烦。好在gradle提供了一个buidlFinished()方法,它可以在编译完成后执行一定的操作,不用理会当前执行的是哪个编译task,只需要把task中代码移到buidlFinished中即可。
project.gradle.buildFinished { println "build finished" }
和buildFinished()对应的还有一个buildStarted()方法,它可以在编译开始之前执行需要的代码。
task相关属性
获取当前task的名字
要在当前任务里获取自己的名字,可以使用其name属性
task ms1{ doLast { println name // ms1 } }
获取当前action的名字
如前所述,action的名字应当只用在内部记录log的时候,目前没有找到方法获取到这个名字。
获取当前运行的gradle任务列表
在gradle脚本中可以通过project.gradle.startParameter.taskNames来获取本次执行的gradle task列表。它得到的是一个数组,即使只执行了一个task。如果没有指定运行任何task,则返回空的数组。
-- build.gradle -- println project.gradle.startParameter.taskNames -- 命令行 -- > gradlew clean build -- 输出 -- [clean, build]
自定义任务属性
可以在任务配置代码中,通过ext.xxx来为任务添加自定义属性。
例如
task myTask { ext.myProperty = "myValue" }
定义好之后就可以在其他代码中通过myTask.myProperty来访问这个属性的值。
默认任务
Gradle 允许在build.gradle脚本中定义一个或多个默认任务,默认任务的意思是没有指定任何任务的时候会执行默认任务。要指定默认任务可以使用defaultTasks关键字。
例如,如下代码指定默认任务为myTask和build,则执行gradlew时如果没有指定任何任务,就会执行myTask和build。
defaultTasks 'myTask', 'build' task myTask { doLast { println "run default task." } }
需要注意的是,使用defaultTasks指定默认任务的时候,任务名一定要加引号,即使它是在任务定义之后声明,也是如此。
覆盖已有的任务
有时我们可能需要覆盖掉一个已有的任务,这个任务可能是自定义的,或者是来自其他Gradle插件中的任务。虽然这种场景并不常见,不过还是有必要了解这种用法,说不定在需要的时候就能帮上大忙。
覆盖已有任务只需要重新创建一个新的同名task,并指定overwrite为true即可。
overwrite的覆盖操作,不仅会覆盖掉原先task的所有action,而且会覆盖原先task指定的type类型,还会覆盖掉所有和原先task相关的依赖关系。被覆盖的依赖关系不仅包含原先的task对其他task的依赖,还包含其他task对原先task的依赖,所有的和原先task相关的依赖关系都会被移除。当然,可以在overwrite之后重新定义新的依赖关系。
overwrite的覆盖操作不会覆盖原先创建task块中的代码,所有创建task代码仍然会被执行。
此外,如果定义overwrite的task在这之前并没有被创建,那么gradle会忽略这个属性,等同于创建一个新的task,不会有错误出现。
如下代码创建了两个名为copy的task,第二个copy task指定了overwrite为true,因此第二个copy task会覆盖第一个。此外,第一个copy task依赖一个名为delete的task,由于这条依赖关系在overwrite之前配置,所以同样会被覆盖。
task delete(type: Delete) { delete "s1.txt" } task copy(type: Copy) { println name from(file('ss.txt')) into(file('new_dir')) doLast { println "I am a copy task" } } copy.dependsOn delete task copy(overwrite: true) { println name doLast { println('I am the new one.') } }
这里执行gradlew copy的完整流程如下。
- 创建delete task,执行语句delete “s1.txt”,注意这里只是执行了delete()方法指定了要删除的文件,并没有真正执行删除操作,删除操作只有执行这个task的时候,才会在delete task的action中执行。
- 创建第一个copy task,依次执行所有代码,即println name, from(file(‘ss.txt’)),into(file(‘new_dir’))和doLast。同样的这里的doLast只是为task添加了action,并不是真正的执行了这个action。
- 执行copy.dependsOn delete为copy添加一个依赖关系。
- 创建第二个copy task,由于overwrite为true,所以会移除原先的copy task,原先copy task所有信息都会丢失,包括依赖关系,包括指定的type,也包括 from(file(‘ss.txt’)),into(file(‘new_dir’))的配置和doLast添加的action。但是println name由于已经执行过了,已经打印出来的内容不会消失不见。然后重新创建新的copy task,执行println name和doLast。
- 执行copy task,由于原先task已经不存在,所以只会执行println(‘I am the new one.’)这个action中的语句。不会执行println “I am a copy task”,也不会执行复制’ss.txt’到new_dir的操作,也不会执行delete task。最终结果就是打印了两次name和一次I am a copy task。
-
Task.Run 和 Task.Factory.StartNew 区别
2018-11-05 08:37:27转:Task.Run 和 Task.Factory.StartNew 区别 Task.Run 是在 dotnet framework 4.5 之后才可以使用, Task.Factory.StartNew 可以使用比 Task.Run 更多的参数,可以做到更多的定制。 可以认为 Task.Run 是... -
MapReduce快速入门系列(11) | MapTask,ReduceTask以及MapReduce运行机制详解
2020-04-28 11:00:04前面我们讲解了MapReduce的Shuffle机制,那么这篇文章博主继续为大家讲解MapTask,ReduceTask和MapReduce运行机制。 目录一. MapTask运行机制详解以及Map任务的并行度二. ReduceTask 工作机制以及reduceTask的并行度... -
Task运行过程分析3——Map Task内部实现
2016-05-15 15:46:51在Task运行过程分析2中提到,MapTask分为4种,分别是Job-setup Task、Job-cleanup Task、Task-cleanup Task和Map Task。其中,Job-setup Task和Job-cleanup Task分别是作业运行时启动的第一个任务和最后一个任务,... -
Task运行过程分析5——ReduceTask内部实现
2016-05-16 16:05:17与MapTask一样,ReduceTask也分为四种,即Job-setup Task,Job-cleanup Task,Task-cleanup Task和Reduce Task。本文重点介绍第四种——普通Reduce Task。 Reduce Task要从各个Map Task上读取一片数据,经排序后,... -
c#编程:Task不包含Task.Run
2017-07-13 14:03:54问题:“System.Threading.Tasks.Task”并不包含“Run”的定义 解决: 使用4.5框架 -
一文搞懂Spark的Task调度器(TaskScheduler)
2020-05-10 13:11:29TaskScheduler的核心任务是提交TaskSet到集群运算并汇报结果。 为TaskSet创建和维护一个TaskSetManager, 并追踪任务的本地性及错误信息。 遇到Straggle任务会放到其他结点进行重试。 向DAGScheduler汇报执行情况, ... -
C#中的thread和task之Task
2017-08-22 16:22:40C#中的thread和task -
MapTask,ReduceTask,MapReduce运行机制详解
2019-11-16 16:14:05...这篇博客,主要针对MapTask与ReduceTask运行机制的一个详解与MapReduce总体运行机制做一个较为详细的介绍! MapTask运行机制详解以及Map任务的并行度 &nb... -
C# Task,new Task().Start(),Task.Run();TTask.Factory.StartNew
2019-09-24 03:59:271. Task task = new Task(() => { MultiplyMethod(a, b); }); task.Start(); 2. Task task = Task.Run(() => { Multiply... -
celery中shared_task和task有啥不同呢?
2020-09-04 14:45:07from celery import shared_task @shared_task def func(): pass shared_task 调用时,就像普通的函数一样调用就行 在什么情况下会用到哪种呢? -
鸿蒙内核源码分析(Task管理篇) | Task是竞争系统资源的最小运行单元 | 中文注解HarmonyOS源码 | v5.05
2020-09-18 11:00:49Task/线程管理篇 提示:本文基于开源鸿蒙内核分析,详细查看进入kernel_liteos_a源码。 本文作者:持续深入研究鸿蒙内核源码,仅代表个人观点,错误之处,欢迎大家指正。 本文分析Task/线程管理源码 详见:los_task.... -
C# Task的用法
2017-02-09 11:19:23C# Task 的用法 其实Task跟线程池ThreadPool的功能类似,不过写起来更为简单,直观。代码更简洁了,使用Task来进行操作。可以跟线程一样可以轻松的对执行的方法进行控制。 顺便提一下,配合... -
Swoole Task Worker进程相关知识讲解---Task
2019-06-15 18:22:33Task 简介 task进程是swoole当中独立于worker进程的工作进程。用于处理一些耗时较长的逻辑,这些逻辑如果在工作当中处理时并不会影响worker进程处理来自于客户端的请求。由此大大提高了swoole扩展的并发能力。 在... -
C# task 取消
2018-12-24 10:27:46我们知道task是并行计算的,比如说主线程在某个时刻由于某种原因要取消某个task的执行,我们能做到吗? 当然我们可以做到。 在4.0中给我们提供一个“取消标记”叫做CancellationTokenSource.Token,在创建task的... -
Linux内核task_struct获取进程Task的名称
2018-02-01 11:12:30在内核中,如果你已经获取到相关Task(进程)的task_struct结构,你可以通过task_struct结构中的 char comm[TASK_COMM_LEN];成员可以获取到进程的名称。 //注意 TASK_COMM_LEN 的长度是16字节 sprintf(buffer,"%s... -
spark executor task执行
2020-01-24 01:56:51Executor执行任务的起点是Executor的launchTask()方法。 val executorData = executorDataMap(task.executorId) executorData.freeCores -= scheduler....logDebug(s"Launching task ${task.taskId} on executor i... -
C# Task用法
2019-02-22 14:38:281、Task的优势 ThreadPool相比Thread来说具备了很多优势,但是ThreadPool却又存在一些使用上的不方便。比如: ◆ ThreadPool不支持线程的取消、完成、失败通知等交互性操作; ◆ ThreadPool不支持线程执行的... -
TASK_INTERRUPTIBLE 和 TASK_UNINTERRUPTIBLE
2014-07-11 12:58:20事象の待ち合わせ(待機状態) 値 意味 TASK_RUNNING 実行可能状態(実行状態、実行待ち状態) TASK_INTERRUPTIBLE 待機状態。...TASK_UNINTERRUPTIBLE ...TASK_RUNNING ...TASK_RUNNNINGと -
MapTask运行机制详解以及Map任务的并行度,ReduceTask 工作机制以及reduceTask的并行度,MapReduce总体工作...
2019-11-15 09:37:01MapTask运行机制详解 整个Map阶段流程大体如图所示 简单概述 inputFile通过split被逻辑切分为多个split文件, 通过Record按行读取内容给map(用户自己实现的)进行处理, 数据被map处理结束之后交给OutputCollector... -
全面理解Gradle - 定义Task
2017-12-26 00:14:00之前我们讲述了Groovy的语法,还讲述了Gradle的执行时序,本篇文章讲述下Task的定义。 Task可以理解为Gradle的执行单元,实在是太重要了。根据前面的分析,Gradle通过一个个task来完成具体的构建任务,下面我们来看... -
Task作为返回值以及Task作为返回值
2019-02-12 10:11:00async await return Task https://stackoverflow.com/questions/25191512/async-await-return-task Can somebody explain what does this means into a synchronous method? If I try to change the method to .... -
c# Task返回值
2017-04-28 14:27:26Task返回值,目前有2种情况,一种是异步async返回值,一种是同步返回值 第一种:异步返回值 Task方法如果加了async关键字,那么就是异步返回方法,如果是异步返回方法,需要返回一个值时,直接return value,就可以... -
MapTask、ReduceTask并行度决定机制
2018-03-28 16:03:47MapTask的并行度决定map阶段的任务处理并发度,进而影响到整个job的处理速度。那么,MapTask并行实例是否越多越好呢?其并行度又是如何决定呢? 1、mapTask并行度的决定机制 一个job的map阶段并行度由客户端在... -
TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE
2014-09-23 16:43:28TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE 的区别 TASK_INTERRUPTIBLE是可以被信号和wake_up()唤醒的,当信号到来时,进程会被设置为可运行。 而TASK_UNINTERRUPTIBLE只能被wake_up()唤醒。 信号本质 信号是在... -
.Net Task常见问题
2016-08-10 14:33:02最近尝试使用一下Task,但是使用过程中因为API的不熟悉碰到了很多问题,不清楚什么时间来调用Task.Start(),具体该怎么使用等等。如下所描述的Task.Start()方法均为实例方法。1. 什么时候使用Task.Start()方法?Task...
-
Qt and Qt Charts
-
量化高频交易系统tick数据采集
-
2010-2020年暨南大学824环境保护概论考研真题
-
商业的本质——杰克·韦尔奇著
-
阿里中间件面试专题:深度解剖kafka高频面试原题
-
掺Er3+晶体近3 μm中红外激光研究进展
-
【数据分析-随到随学】Spark理论及实战
-
算法导论(基础知识)——编程大牛的必经之路
-
Metabase从入门到精通视频教程
-
jquery中怎么获取数组的长度
-
在windows10下的ubuntu启动docker
-
Kotlin协程极简入门与解密
-
基于深度法线纹理检测使用Robert算子的边缘检测——UnityShader学习笔记
-
金三银四跳槽季,Java后端面试题总结:【字节跳动+腾讯面试】含答案
-
flutter插件调用APP页面、使用原生aar,framework库
-
Dubbo.pptx
-
21年新消息队列RabbitMQ视频教程AMQP教程
-
在VMWare虚拟机上安装Kali linux系统的完整过程(图文)
-
电商设计专业思维
-
微信支付2021系列之付款码支付一学就会java版