【翻译】kotlin协程核心库文档(一)—— 协程基础

github原文地址

原创翻译,转载请保留或注明出处:https://www.jianshu.com/p/7497bce754eb

协程基础


本小节涵盖了协程的基础概念

第一个协程

运行以下代码:

fun main(args: Array) {
    launch { // launch new coroutine in background and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello,") // main thread continues while coroutine is delayed
    Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}

获取完整代码 here

运行结果:

Hello,
World!

实质上,协程是轻量级的线程。它们使用 “launch” 协程构建器启动 ,你也可以通过如下方式获得同样的结果:替换 launch { ... }thread { ... }, 替换delay(...)Thread.sleep(...)

连接阻塞、非阻塞的世界

第一个示例混合了非阻塞的 delay(...) 与 阻塞的 Thread.sleep(...) 在同一块代码中。这很容易让人迷惑哪一处是阻塞的哪一处不是。我们可以通过使用 runBlocking 协程构建器来明确阻塞:

fun main(args: Array) {
    launch { // launch new coroutine in background and continue
        delay(1000L)
        println("World!")
    }
    println("Hello,") // main thread continues here immediately
    runBlocking { // but this expression blocks the main thread
        delay(2000L) // ... while we delay for 2 seconds to keep JVM alive
    }
}

获取完整代码 here

结果是相同的,但是这段代码使用了非阻塞的delay。调用runBlocking的主线程阻塞了,直到内部协程runBlocking完成。

这个示例同样可以用一种更通顺的方式改写:使用runBlocking来封装主函数的执行:

fun main(args: Array) = runBlocking { // start main coroutine
    launch { // launch new coroutine in background and continue
        delay(1000L)
        println("World!")
    }
    println("Hello,") // main coroutine continues here immediately
    delay(2000L) // delaying for 2 seconds to keep JVM alive
}

获取完整代码 here

这里runBlocking { ... }作为一个适配器,用来启动顶层主协程。 我们明确地指定它的返回类型为Unit,因为一个格式良好的Kotlin主函数必须返回 Unit

这同样也是为挂起函数编写单元测试的一种方式:

class MyTest {
    @Test
    fun testMySuspendingFunction() = runBlocking {
        // here we can use suspending functions using any assertion style that we like
    }
}

等待一个任务

当另一个协程正在工作时,延迟一段时间并不是一个好的方式。我们可以通过明确地方式等待(以非阻塞的方式),直到我们之前启动的后台任务完成:

fun main(args: Array) = runBlocking {
    val job = launch { // launch new coroutine and keep a reference to its Job
        delay(1000L)
        println("World!")
    }
    println("Hello,")
    job.join() // wait until child coroutine completes
}

获取完整代码 here

现在结果是同样的,但是主协程的代码不以任何方式与后台任务的执行时间相关联。

提取函数进行重构

我们可以提取launch { ... }中的代码块到另一个分离的函数中。当你对这段代码进行“提取函数”重构时,你会得到一个带有 suspend 修饰符的新函数。这是你的第一个挂起函数。挂起函数可以像普通函数一样在协程中使用,但他们的附加功能是,他们可以反过来使用其他挂起函数。类似这个示例中的delay,它挂起了一个协程的执行过程。

fun main(args: Array) = runBlocking {
    val job = launch { doWorld() }
    println("Hello,")
    job.join()
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}

获取完整代码 here

协程是轻量的

执行以下代码:

fun main(args: Array) = runBlocking {
    val jobs = List(100_000) { // launch a lot of coroutines and list their jobs
        launch {
            delay(1000L)
            print(".")
        }
    }
    jobs.forEach { it.join() } // wait for all jobs to complete
}

获取完整代码 here

它启动了10万个协程,并在一秒后,每个协程打印了一个“.”。骚年,你敢用线程做这样的事么?(很可能你的代码会产生一些内存溢出的错误)。

协程类似守护线程

下面的代码启动了一个长时间运行的协程,它每秒打印两次“I'm sleeping”,在一段延迟后从主函数返回:

fun main(args: Array) = runBlocking {
    launch {
        repeat(1000) { i ->
            println("I'm sleeping $i ...")
            delay(500L)
        }
    }
    delay(1300L) // just quit after delay
}

获取完整代码 here

你可以运行并看到它打印了三行然后终止:

I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...

激活的协程并不会使进程保持活跃状态,它们就像守护线程一样。

你可能感兴趣的:(【翻译】kotlin协程核心库文档(一)—— 协程基础)