kotlin协程生命周期-基础

一、Job生命周期

通过

 1.Job与async

CoroutineScope的扩展函数launch返回一个Job对象。

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
public interface Job : CoroutineContext.Element {
    
    public companion object Key : CoroutineContext.Key<Job>

    public val isActive: Boolean
	......
}

CoroutineScope的扩展函数async返回一个Deferred对象,Deferred 继承 Job。

public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyDeferredCoroutine(newContext, block) else
        DeferredCoroutine<T>(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

Deferred 继承 Job,同样是一个接口,Deferred 有一个泛型参数T,并且有一个关键的await()方法,其返回值类型是T。

public interface Deferred<out T> : Job {

    public suspend fun await(): T
	......
}

2.job.cancel()

fun main() {
    runBlocking {
        val job = launch {
            println("launch start")
            delay(2000L)
            println("launch end")
        }
        job.printlog()
        job.cancel()
        job.printlog()
    }
}



fun Job.printlog() {
    printCoroutine("isActive:$isActive, isCancelled:$isCancelled, isCompleted:$isCompleted")
}


fun printCoroutine(any: Any?) {
    println("" + any + ";Thread:" + Thread.currentThread().name)
}

输出log 如下:

isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
isActive:false, isCancelled:true, isCompleted:false;Thread:main @coroutine#1

上面的代码中,通过job.cancel()取消协程任务,然后通过job.isCancelled查看状态为true。代表了协程任务处于取消状态。所以launch中的协程任务没有执行就取消了。

对上面的代码做简单修改:在job执行cancel前delay 1000ms,可以发现launch任务执行过程中被取消了。

 runBlocking {
        val job = launch {
            println("launch start")
            delay(2000L)
            println("launch end")
        }
        job.printlog()
        delay(1000L)
        job.cancel()
        job.printlog()
    }

输出log:

isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
launch start
isActive:false, isCancelled:true, isCompleted:false;Thread:main @coroutine#1

如果在上述代码中delay(2000L)后再次执行  job.printlog()。可以发现此时:isCompleted:true。

    runBlocking {
        val job = launch {
            println("launch start")
            delay(2000L)
            println("launch end")
        }
        job.printlog()
        delay(1000L)
        job.cancel()
        job.printlog()
        delay(2000)
        job.printlog()
    }

Log输出如下:

isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
launch start
isActive:false, isCancelled:true, isCompleted:false;Thread:main @coroutine#1
isActive:false, isCancelled:true, isCompleted:true;Thread:main @coroutine#1

 3.job.start()

fun main() {
      runBlocking {
        val job = launch(start = CoroutineStart.LAZY) {
            printCoroutine("Coroutinue start!")
            delay(2000L)
            printCoroutine("Coroutinue end!")
        }
        delay(1000L)
        job.printlog()
        job.start()
        job.printlog()
        delay(1000L)
        job.cancel()
        delay(1000L)
        job.printlog()
    }
}


结果:
isActive:false, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
Coroutinue start!;Thread:main @coroutine#2
isActive:false, isCancelled:true, isCompleted:true;Thread:main @coroutine#1

将启动模式设置为Coroutinue.LAZY,协程任务被launch以后,可以发现isActive为false,不会立即执行,当执行job.start()之后,isActive为true,所以Coroutinue.LAZY是一种懒加载的行为模式。所以通过Log可以发现只有调用了job.start()以后,job的状态才变成了Active。调用了cancel以后,job的状态才会变成isCancelled、isCompleted。

协程的Job有两种初始状态,如果Job是以懒加载的方式创建,那么它的初始状态是New;如果协程以非懒加载的方式创建,它的初始状态是Active。

注意:当调用cancel以后,isCancelled = true,然后isCompleted = true,说明对于协程来说,取消协程,也是一种“结束状态”。

New、Active、Completing、Cancelling、Completed、Cancelled 这些状态,都是 Job 内部私有的状态。而 Job 对外暴露出的 isCompleted 并不是与其一一对应的。Job 内部私有的 Completed、Cancelled 状态,都会认为是外部的 isCompleted 状态。

4.协程正常结束的生命周期

fun main() {
       runBlocking {
        val job = launch(start = CoroutineStart.LAZY) {
            printCoroutine("Coroutinue start!")
            delay(1000L)
            printCoroutine("Coroutinue end!")
        }
        delay(500L)
        job.printlog()
        job.start()
        job.printlog()
        delay(2000L)
        job.printlog()
        delay(2000L)
        printCoroutine("Process end!")
    }
}


输出:
isActive:false, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
Coroutinue start!;Thread:main @coroutine#2
Coroutinue end!;Thread:main @coroutine#2
isActive:false, isCancelled:false, isCompleted:true;Thread:main @coroutine#1
Process end!;Thread:main @coroutine#1

 从Log可以发现:isActive从false到true;isCompleted从false到true。

5.Job内部delay大于外部delay

runBlocking {
        val job = launch(start = CoroutineStart.LAZY) {
            printCoroutine("Coroutinue start!")
            delay(6000L)
            printCoroutine("Coroutinue end!")
        }
        delay(500L)
        job.printlog()
        job.start()
        job.printlog()
        delay(1000L)
        job.printlog()
        delay(1000L)
        printCoroutine("Process end!")
    }


输出:
isActive:false, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
Coroutinue start!;Thread:main @coroutine#2
isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
Process end!;Thread:main @coroutine#1
Coroutinue end!;Thread:main @coroutine#2

 在上面的代码中,将job内部的delay改为6000L后,runBlocking会一直阻塞,等到job任务执行完毕以后才能真正退出。

6.等待和监听协程的结束事件

   runBlocking {
        suspend fun download() {
            printCoroutine("Start Download...")
            delay(4000L)
            printCoroutine("End Download...")
        }

        val job = launch(start = CoroutineStart.LAZY) {
            download()
        }
        delay(200L)
        job.printlog()
        job.start()
        job.printlog()
        job.invokeOnCompletion {
            job.printlog()
        }
        job.join()
        printCoroutine("Process End!")
    }


Log:
isActive:false, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
isActive:true, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
Start Download...;Thread:main @coroutine#2
End Download...;Thread:main @coroutine#2
isActive:false, isCancelled:false, isCompleted:true;Thread:main @coroutine#2
Process End!;Thread:main @coroutine#1

通过Log可以发现,invokeOnCompletion{}可以用来监听协程结束的事件,即使job取消,invokeOnCompletion{}仍然会被调用。

在上面代码中job.start()执行以后执行job.cancel()。可以发现:即使job取消,invokeOnCompletion{}仍然会被调用。

runBlocking {
        suspend fun download() {
            printCoroutine("Start Download...")
            delay(4000L)
            printCoroutine("End Download...")
        }

        val job = launch(start = CoroutineStart.LAZY) {
            download()
        }
        delay(200L)
        job.printlog()
        job.start()
        job.cancel()
        job.printlog()
        job.invokeOnCompletion {
            job.printlog()
        }
        job.join()
        printCoroutine("Process End!")
    }


isActive:false, isCancelled:false, isCompleted:false;Thread:main @coroutine#1
isActive:false, isCancelled:true, isCompleted:false;Thread:main @coroutine#1
isActive:false, isCancelled:true, isCompleted:true;Thread:main @coroutine#2
Process End!;Thread:main @coroutine#1

 job.join() 其实是一个“挂起函数”,它的作用就是:挂起当前的程序执行流程,等待 job 当中的协程任务执行完毕,然后再恢复当前的程序执行流程。

7.Job源码



public interface Job : CoroutineContext.Element {

   
    // ------------ 状态查询API ------------

    public val isActive: Boolean

    public val isCompleted: Boolean

    public val isCancelled: Boolean

    public fun getCancellationException(): CancellationException

    // ------------ 操控状态API ------------

    public fun start(): Boolean

    public fun cancel(cause: CancellationException? = null)

    public fun cancel(): Unit = cancel(null)

    public fun cancel(cause: Throwable? = null): Boolean

    // ------------ 等待状态API ------------

    public suspend fun join()

    public val onJoin: SelectClause0

    // ------------ 完成状态回调API ------------

    public fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle

    public fun invokeOnCompletion(
        onCancelling: Boolean = false,
        invokeImmediately: Boolean = true,
        handler: CompletionHandler): DisposableHandle

}

8.Deferred

await()方法

 runBlocking {
        val deferred = async {
            printCoroutine("Coroutinue Start!")
            delay(1000L)
            printCoroutine("Coroutinue End!")
            "result"
        }
        val result = deferred.await()
        println("Result:$result")
        printCoroutine("Process End!")
    }

Coroutinue Start!;Thread:main @coroutine#2
Coroutinue End!;Thread:main @coroutine#2
Result:result
Process End!;Thread:main @coroutine#1

deferred.await() 可以获取协程的执行结果,它还会阻塞当前协程的执行流程,直到协程任务执行完毕。在这一点的行为上,await() 和 join() 是类似的。

public interface Deferred<out T> : Job {

    public suspend fun await(): T
	... ...
}

await() 是一个挂起函数,拥有挂起和恢复的能力。如果当前的 Deferred 任务还没执行完毕,那么,await() 就会挂起当前的协程执行流程,等待 Deferred 任务执行完毕,再恢复执行后面剩下的代码。而job.join() 在协程执行完毕之前,后面的协程代码都被暂时挂起了,等到协程执行完毕,才有机会继续执行。

二、Job 与结构化并发

 runBlocking {
        val parentJob: Job
        var job1: Job? = null
        var job2: Job? = null
        var job3: Job? = null

        parentJob = launch {
            job1 = launch {
                delay(1000L)
            }

            job2 = launch {
                delay(2000L)
            }

            job3 = launch {
                delay(3000L)
            }
        }
        delay(500L)
        parentJob.children.forEachIndexed { index, job ->
            when (index) {
                0 -> println("job1 === job is ${job1 === job}")
                1 -> println("job2 === job is ${job2 === job}")
                2 -> println("job2 === job is ${job3 === job}")
            }
        }
        parentJob.join()
        printCoroutine("Process End!")
    }


job1 === job is true
job2 === job is true
job2 === job is true
Process End!;Thread:main @coroutine#1

parentJob 是最外层的 launch 返回的对象,在这个 launch 的内部,嵌套了三个 launch,它们的 Job 对象分别赋值给了 job1、job2、job3。在parentJob.children遍历过程中,发现job1、job2、job3 其实就是 parentJob 的 children。也就是说,我们使用 launch 创建出来的协程,是存在父子关系的。

public interface Job : CoroutineContext.Element {
 

    // ------------ parent-child ------------

    public val children: Sequence<Job>

    @InternalCoroutinesApi
    public fun attachChild(child: ChildJob): ChildHandle
}

 每个 Job 对象,都会有一个Sequence类型的 children 属性,是一个惰性的集合,我们可以对它进行遍历。而 attachChild() 则是一个协程内部的 API,用于绑定 ChildJob 。

通过调用parentJob 的 join() 方法,会等待其内部的 job1、job2、job3 全部执行完毕,才会恢复执行。

 runBlocking {
        val parentJob: Job
        var job1: Job? = null
        var job2: Job? = null
        var job3: Job? = null

        parentJob = launch {
            job1 = launch {
                printCoroutine("job1 start")
                delay(1000L)
                printCoroutine("job1 end")
            }

            job2 = launch {
                printCoroutine("job2 start")
                delay(2000L)
                printCoroutine("job2 end")
            }

            job3 = launch {
                printCoroutine("job3 start")
                delay(3000L)
                printCoroutine("job3 end")
            }
        }
        delay(500L)
        parentJob.children.forEachIndexed { index, job ->
            when (index) {
                0 -> println("job1 === job is ${job1 === job}")
                1 -> println("job2 === job is ${job2 === job}")
                2 -> println("job2 === job is ${job3 === job}")
            }
        }
        parentJob.cancel()
        printCoroutine("Process End!")
    }


job1 start;Thread:main @coroutine#3
job2 start;Thread:main @coroutine#4
job3 start;Thread:main @coroutine#5
job1 === job is true
job2 === job is true
job2 === job is true
Process End!;Thread:main @coroutine#1

parentJob 调用cancel() 方法,job1、job2、job3的协程任务也全都被取消了。

三、async使用场景

runBlocking {
        suspend fun getResult1(): String {
            delay(1000L)
            return "Result1"
        }

        suspend fun getResult2(): String {
            delay(1000L)
            return "Result2"
        }

        suspend fun getResult3(): String {
            delay(1000L)
            return "Result3"
        }

        val results = mutableListOf<String>()

        val time = measureTimeMillis {
            results.add(getResult1())
            results.add(getResult2())
            results.add(getResult3())
        }
        println("Time:$time")
        println(results)
    }


Time:3052
[Result1, Result2, Result3]

函数总耗时未3052毫秒,可以使用async进行优化。

    runBlocking {
        suspend fun getResult1(): String {
            delay(1000L)
            return "Result1"
        }

        suspend fun getResult2(): String {
            delay(1000L)
            return "Result2"
        }

        suspend fun getResult3(): String {
            delay(1000L)
            return "Result3"
        }

        val results = mutableListOf<String>()

        val time = measureTimeMillis {

            val result1 = async {
                getResult1()
            }
            val result2 = async {
                getResult1()
            }
            val result3 = async {
                getResult1()
            }

            results.add(result1.await())
            results.add(result2.await())
            results.add(result3.await())
        }
        println("Time:$time")
        println(results)
    }

Time:1029
[Result1, Result1, Result1]

可以看到 async可以用来优化一些并发操作。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin协程是一种用于实现轻量级、非阻塞并发的编程框架。它允许您以顺序的方式编写异步代码,同时提供了一种简单的方式来管理和组织协程的执行。 在Kotlin中,协程的管理是通过CoroutineScope来实现的。CoroutineScope是一个接口,用于定义协程的作用域。通过实现CoroutineScope接口,您可以在作用域内启动和管理协程。 为了创建一个协程作用域,您可以使用CoroutineScope的构造函数,并为其提供一个CoroutineContext。CoroutineContext是一个上下文对象,它包含了协程的执行环境和其他相关信息。在协程作用域中,您可以使用该上下文对象来控制协程的行为。 一旦您创建了协程作用域,您可以使用该作用域来启动协程。通过调用CoroutineScopelaunch或async函数,您可以在作用域内启动一个新的协程。这些函数将返回一个Job对象,您可以使用该对象来管理和控制协程的执行。 在协程作用域中,您还可以使用CoroutineScope的其他函数来管理协程生命周期。例如,您可以使用cancel函数来取消作用域内的所有协程,使用join函数来等待所有协程的完成。 总结起来,Kotlin协程的管理是通过CoroutineScope来实现的。通过实现CoroutineScope接口并使用其函数,您可以在作用域内启动和管理协程的执行。使用CoroutineScope的构造函数和上下文对象,您可以控制协程的行为。同时,您还可以使用其他函数来管理协程生命周期

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值