kotlin协程介绍(一)

时间:2023-02-06 12:24:08

问题背景

我们在kotlin的学习和使用过程中,协程是一个很重要的概念,这个也是java没有的,那么,什么是协程呢? 协程的定义可以将它理解成一种轻量级的线程。我们之前所学习的线程是非常重量级的,它需要依靠操作系统的调度才能实现不同线程之间的切换。使用协程就可以在编程语言的层面实现不同协程之间的切换,大大提升并发编程的运行效率

问题分析

大概了解了协程的概念之后,第二个问题,协程是怎么使用的呢? 1、导入依赖库 想要使用协程功能,需要先在app/build.gradle文件当中添加如下依赖库:

    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"

2、简单启动一个协程

fun main() {
    GlobalScope.launch {
        println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
    }
}

运行结果如下: kotlin协程介绍(一) Global.launch函数每次创建的都是一个顶层协程,这种协程当应用程序运行结束时也会跟着一起结束。刚才的代码所以无法打印出来,就是因为代码块中的代码还没来得及运行,应用程序就结束了。 现在我们让程序延迟一段时间再结束,代码如下所示:

fun main() {
    GlobalScope.launch {
        println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
    }
    Thread.sleep(1000)
}

运行结果如下: kotlin协程介绍(一) 这种写法还是存在问题的,如果代码块中的代码在1秒钟之内不能运行结束,那么就会被强制中断。比如下面代码:

fun main() {
    GlobalScope.launch {
        println("Coroutines in Thread.currentThread().name begin" + Thread.currentThread().name)
        delay(1500)
        println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
    }
    Thread.sleep(1000)
}

运行结果如下: kotlin协程介绍(一) 协程中代码执行到一半就被强行中止了,这个问题怎么处理呢? 借助runBlocking函数就可以实现这个功能,代码如下:

fun main() {
    runBlocking {
        println("Coroutines in Thread.currentThread().name begin" + Thread.currentThread().name)
        delay(1500)
        println("Coroutines in Thread.currentThread().name" + Thread.currentThread().name)
    }
    Thread.sleep(1000)
}

运行结果如下: kotlin协程介绍(一) 3、创建多个协程 使用launch函数,代码如下:

fun main() {
    runBlocking {
        launch {
            println("launch1")
            delay(1000)
            println("launch1 finished")
        }
        launch {
            println("launch2")
            delay(1000)
            println("launch2 finished")
        }
    }
}

运行结果如下: kotlin协程介绍(一) 可以看到,两个子协程中的日志是交替打印的,说明它们确实是像多线程那样并发运行的。然而这两个子协程实际却运行在同一个线程当中,只是由编程语言来决定如何在多个协程之间进行调度,不需要操作系统参与,这也就使得协程的并发效率会高得多。 不过,随着launch函数中的逻辑越来越复杂,可能你需要将部分代码提取到一个单独的函数中。为此Kotlin提供了一个suspend关键字,使用它可以将任意函数声明成挂起函数,而挂起函数之间都是可以互相调用的,如下所示:

suspend fun printHello(){
    println("hello")
    delay(1000)
}

suspend函数只能在协程中或者另外一个suspend函数中调用,调用suspend函数时,协程会挂起,即从当前的线程脱离,等这个函数执行完毕再切回原线程继续执行。 4、协程取消与超时 一个页面启动了一个协程,当页面关掉时我们需要取消协程,要不然等协程切回主线程更新UI时就会出错,还有一个场景就是我们在做网络请求时,通常需要设置一个超时时间。下面是协程取消的例子,代码如下:

    val job = Job()
    val scope = CoroutineScope(job)
    scope.launch { 
        //处理具体逻辑
    }
    job.cancel()

下面是协程超时的例子,代码如下:

GlobalScope.launch(Dispatchers.Main) {
    var user = doIO()
    Log.v("Tag", "result $user")
}
 
suspend fun doIO(): String? {
    var res = withContext(Dispatchers.IO) {
        withTimeoutOrNull(4000) {
            delay(3000)
            "done"
        }
    }
    return res
}

上面是一个超时的例子,withTimeoutOrNull()函数表示,如果在指定的时间内完成了工作,就返回下面的结果("done"),如果未能完成的话会返回null。 5、async和await 协程在切线程后或者调用suspend函数后,虽然没有阻塞协程所在线程,但协程是被阻塞的(挂起),例如在一个协程内需要做两个不相关的请求,他们执行起来也是串行的,使用async函数即可并行的执行:async函数必须在协程作用域当中才能调用,它会创建一个新的子协程并返回一个Deferred对象,如果我们想要获取async函数代码块的执行结果,只需要调用Deferred对象的await()方法即可,代码如下所示:

fun main() {
    runBlocking {
        val result = async {
            1 + 1
        }.await()
        println(result)
    }
}

运行结果如下: kotlin协程介绍(一)

问题总结

本文主要介绍了部分kotlin中协程的相关概念,包括什么是协程、如果创建一个协程、创建多个协程、协程的取消和定时、以及async等关键字的介绍,有兴趣的同学可以进一步深入研究。