精华内容
下载资源
问答
  • Task.CompletedTaskTask.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:57
    1,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的时候还可以指定一些参数。比较重要的有:

    1. type: task类型
    2. dependsOn:依赖的task列表
    3. group:task所属的组

    其他参数可以参见 https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/TaskContainer.html#create-java.util.Map-

    为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的完整流程如下。

    1. 创建delete task,执行语句delete “s1.txt”,注意这里只是执行了delete()方法指定了要删除的文件,并没有真正执行删除操作,删除操作只有执行这个task的时候,才会在delete task的action中执行。
    2. 创建第一个copy task,依次执行所有代码,即println name, from(file(‘ss.txt’)),into(file(‘new_dir’))和doLast。同样的这里的doLast只是为task添加了action,并不是真正的执行了这个action。
    3. 执行copy.dependsOn delete为copy添加一个依赖关系。
    4. 创建第二个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。
    5. 执行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的Shuffle机制,那么这篇文章博主继续为大家讲解MapTask,ReduceTask和MapReduce运行机制。 目录一. MapTask运行机制详解以及Map任务的并行度二. ReduceTask 工作机制以及reduceTask的并行度...
  • Task运行过程分析2中提到,MapTask分为4种,分别是Job-setup Task、Job-cleanup TaskTask-cleanup Task和Map Task。其中,Job-setup Task和Job-cleanup Task分别是作业运行时启动的第一个任务和最后一个任务,...
  • 与MapTask一样,ReduceTask也分为四种,即Job-setup Task,Job-cleanup TaskTask-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框架
  • TaskScheduler的核心任务是提交TaskSet到集群运算并汇报结果。 为TaskSet创建和维护一个TaskSetManager, 并追踪任务的本地性及错误信息。 遇到Straggle任务会放到其他结点进行重试。 向DAGScheduler汇报执行情况, ...
  • C#中的thread和taskTask

    万次阅读 2017-08-22 16:22:40
    C#中的thread和task
  • MapTask,ReduceTask,MapReduce运行机制详解

    千次阅读 2019-11-16 16:14:05
      ...这篇博客,主要针对MapTask与ReduceTask运行机制的一个详解与MapReduce总体运行机制做一个较为详细的介绍! MapTask运行机制详解以及Map任务的并行度     &nb...
  • 1. Task task = new Task(() => { MultiplyMethod(a, b); }); task.Start(); 2. Task task = Task.Run(() => { Multiply...
  • from celery import shared_task @shared_task def func(): pass shared_task 调用时,就像普通的函数一样调用就行 在什么情况下会用到哪种呢?
  • Task/线程管理篇 提示:本文基于开源鸿蒙内核分析,详细查看进入kernel_liteos_a源码。 本文作者:持续深入研究鸿蒙内核源码,仅代表个人观点,错误之处,欢迎大家指正。 本文分析Task/线程管理源码 详见:los_task....
  • C# Task的用法

    万次阅读 2017-02-09 11:19:23
    C# Task 的用法 其实Task跟线程池ThreadPool的功能类似,不过写起来更为简单,直观。代码更简洁了,使用Task来进行操作。可以跟线程一样可以轻松的对执行的方法进行控制。 顺便提一下,配合...
  • Task 简介 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:51
    Executor执行任务的起点是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:28
    1、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阶段流程大体如图所示 简单概述 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:00
    async 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:26
    Task返回值,目前有2种情况,一种是异步async返回值,一种是同步返回值 第一种:异步返回值 Task方法如果加了async关键字,那么就是异步返回方法,如果是异步返回方法,需要返回一个值时,直接return value,就可以...
  • MapTask、ReduceTask并行度决定机制

    千次阅读 2018-03-28 16:03:47
    MapTask的并行度决定map阶段的任务处理并发度,进而影响到整个job的处理速度。那么,MapTask并行实例是否越多越好呢?其并行度又是如何决定呢? 1、mapTask并行度的决定机制 一个job的map阶段并行度由客户端在...
  • TASK_INTERRUPTIBLE 和TASK_UNINTERRUPTIBLE

    千次阅读 2014-09-23 16:43:28
    TASK_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...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 281,720
精华内容 112,688
关键字:

task