Kotlin 协程四

Kotlin 协程四

首页角色扮演代号光更新时间:2024-08-01

主要介绍了协程的取消和超时。这一节主要介绍协程挂机方法的同步、异步;协程上下文和协程调度。

同步

假设我们在别处定义了两个挂起函数,它们各自做一些有用的事情,比如服务器接口调用或费时计算,代码如下:

suspend fun one(): Int { delay(1000L) return 1 } suspend fun two(): Int { delay(1000L) return 2 } fun main() { runBlocking { val time = measureTimeMillis { val resultOne = one() val resultTwo = two() println("The answer is ${resultOne resultTwo}") } println("Completed in $time ms") } }

执行结果

The answer is 3 Completed in 2035 ms

使用正常的顺序调用,因为协程中的代码,就像常规代码一样,默认情况下是顺序。通过执行结果的时间,可以看出总耗时大于one two。

异步

如果我们想通过并发执行两者来更快地得到答案,该怎么办?这就是异步派上用场的地方。

使用async关键字它启动一个单独的协程,与所有其他协程并发工作。与launch区别在于,launch返回一个Job,不携带任何结果值,而async返回一个Deferred(一个轻量级的非阻塞Future,稍后提供结果),通过调用.await()来获得其最终结果。

Deferred也是一个Job,因此可以在需要时取消它。

还是01那段代码,调整main的部分代码如下:

fun main() { runBlocking { val time = measureTimeMillis { val resultOne = async { one() } val resultTwo = async { two() } println("The answer is ${resultOne.await() resultTwo.await()}") } println("Completed in $time ms") } }

执行结果

The answer is 3 Completed in 1032 ms

通过结果可以看出,两个协程同时执行。

异步惰性启动

async可以通过将其开始参数设置为Coroutinstart.LAZY而变得懒惰。在这种模式下,它只在await需要其结果时启动协程,或者当它的Job的start函数被调用时才启动协程。运行示例如下:

fun main() { runBlocking { val time = measureTimeMillis { val one = async(start = CoroutineStart.LAZY) { one() } val two = async(start = CoroutineStart.LAZY) { two() } one.start() // start one two.start() // start two println("The answer is ${one.await() two.await()}") } println("Completed in $time ms") } }

执行结果:

The answer is 3 Completed in 1033 ms

这里定义了两个协程,但没有像02的例子中那样执行,而是通过调用start来控制程序何时开始执行。首先启动一个,然后启动两个,然后等待各个协同程序完成。

上下文和调度

协程总是在由CoroutineContext类型的值表示的上下文中执行,该值在Kotlin标准库中定义。

协程上下文包括一个协程Dispatcher(参见CoroutineDispatcher),它确定相应的协程执行使用哪个线程。协程分派器可以将协程的执行限制在特定的线程上,也可以将它分派到线程池,或者让它不受限制地运行。

所有协程构建器(如launchasync)都接受一个可选的CoroutineContext参数,该参数可用于显式地为新协程和其他上下文元素指定Dispatcher

执行下面这段代码:

fun main() { runBlocking { launch { println("main : I'm working in thread ${Thread.currentThread().name}") } launch(Dispatchers.Unconfined) { println("Unconfined : I'm working in thread ${Thread.currentThread().name}") } launch(Dispatchers.Default) { println("Default : I'm working in thread ${Thread.currentThread().name}") } launch(newSingleThreadContext("OwnThread")) { println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}") } } }

执行结果:

Unconfined : I'm working in thread main Default : I'm working in thread DefaultDispatcher-worker-1 main : I'm working in thread main newSingleThreadContext: I'm working in thread OwnThread

launch{…}在不带参数的情况下使用,它将从启动它的CoroutineScope继承上下文(以及分派器)。在这种情况下,它继承了在主线程中运行的主runBlocking协程的上下文。

Dispatchers.Unconfined是一种特殊的调度程序,它看起来也在主线程中运行,但实际上,它是一种不同的机制。Dispatchers.Unconfined在调用者线程中启动协程,但仅在第一个挂起点之前。挂起之后,它恢复线程中的协程,该协程完全由被调用的挂起函数决定。不受限调度程序适用于既不消耗CPU时间也不更新局限于特定线程的任何共享数据(如UI)的协程。

当作用域中没有显式指定其他分派器时,将使用默认分派器。它由Dispatchers.Default表示,并使用共享后台线程池。

newSingleThreadContext创建要运行的协程的线程。专用线程是一种非常昂贵的资源,在我们真正的应用程序中,一定要在不使用的时候,及时的释放掉。

协程作用域

从讲协程开始,就一直绕不开协程的作用域,那么协程的作用域到底能给我们提供什么呢?

假设我们的应用程序有一个具有生命周期的对象,但该对象不是协程。例如,我们正在编写一个Android应用程序,并在Android活动的上下文中启动各种协程来执行异步操作,以获取和更新数据,制作动画等。当活动被销毁时,所有这些协程都必须被取消,以避免内存泄漏。当然我们可以手动操作上下文和作业来绑定活动及其协程的生命周期,但是kotlinx.coroutines提供了一个抽象封装:CoroutineScope

我们已经熟悉了协程作用域,因为所有的协程构建器都对它的扩展。

接下来,我们通过创建一个绑定到活动生命周期的CoroutineScope实例来管理协程的生命周期。CoroutineScope实例可以由CoroutineScope()或MainScope()工厂函数创建。前者创建一个通用作用域,而后者为UI应用程序创建一个作用域并使用Dispatchers.Main作为默认调度器。代码如下:

class Activity { private val mainScope = MainScope() fun destroy() { mainScope.cancel() } // ToDo ... }

现在,我们可以使用定义的范围在这个Activity的范围内启动协程。在演示中,我们启动了10个延迟不同时间的协程:

fun doSomething() { // launch ten coroutines for a demo, each working for a different time repeat(10) { i -> mainScope.launch { delay((i 1) * 200L) // variable delay 200ms, 400ms, ... etc println("Coroutine $i is done") } } }

在main函数中,我们创建了这个活动,调用doSomething函数,并在500ms后销毁这个活动,这将取消从doSomething启动的所有协程。

val activity = Activity() activity.doSomething() println("Launched coroutines") delay(500L) println("Destroying activity!") activity.destroy() delay(1000)

执行结果:

Launched coroutines Coroutine 0 is done Coroutine 1 is done Destroying activity!

通过执行结果,我们可以看到这一点,因为在销毁活动之后,即使我们再等待一段时间,也不会打印更多的消息。

查看全文
大家还看了
也许喜欢
更多游戏

Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved