基础协程概念

协程是可暂停计算的一个实例。

协程在概念上与线程相似,但协程并不约束于任一线程(可以在不同的线程中挂起和唤醒)

第一份协程代码

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello") // main coroutine continues while a previous one is delayed
}
1
2
3
4
5
6
7
8
9

launchopen in new window 是协程的创建器(builder) 可以启动一个独立工作的协程

delayopen in new window 是一种特殊的suspend函数 可以挂起协程但不阻塞当前线程

runBlockingopen in new window 也是协程的builder 用于连接非协程域和协程域 类型为CoroutineScope

runBlocking 构建一个阻塞当前线程的协程域

结构化并发

协程遵循结构化并发的原则 即新的协程只能在一个特定的CoroutineScope中启动

提取函数

如何将launch块中的语句提取成一个函数?

import kotlinx.coroutines.*

fun main() = runBlocking { // this: CoroutineScope
    launch { doWorld() }
    println("Hello")
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
1
2
3
4
5
6
7
8
9
10
11
12

结合上例,可以看出提取函数的特点 需要在协程中执行的函数 需要添加 suspend 修饰符

在IDE中 可以通过 Extract funcion 功能提取函数

suspend 函数跟常规函数类似 只是它们还能调用 suspend 函数(suspend函数可以相互间调用)

Scope builder

可以使用 coroutineScope 声明自己的协程域

coroutineScope 会创建一个协程域 当域内所有协程结束时它才会结束

coroutineScope 和 runBlocking 的主要区别是 后者(常规函数)会阻塞当前线程 而前者(suspend函数)不会 但会挂起外部协程

可以在任何suspend函数中使用coroutineScope 可以将上例改为这样

fun main() = runBlocking {
    doWorld()
}

suspend fun doWorld() = coroutineScope {  // this: CoroutineScope
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello")
}
1
2
3
4
5
6
7
8
9
10
11

并发

coroutineScope 可以在任何 suspend 函数中用来执行多并发操作 例如下例中同时运行两个协程

// Concurrently executes both sections
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
    launch {
        delay(2000L)
        println("World 2")
    }
    launch {
        delay(1000L)
        println("World 1")
    }
    println("Hello")
}
1
2
3
4
5
6
7
8
9
10
11
12

这两个协程会同时执行

显式 job

一个 launch 协程builder 返回一个 Job 对象

该对象可以处理这个已启动的协程 也可以用于显式等待 join

val job = launch { // launch a new coroutine and keep a reference to its Job
    delay(1000L)
    println("World!")
}
println("Hello")
job.join() // wait until child coroutine completes
println("Done") 
// 输出:
// Hello
// World!
// Done
1
2
3
4
5
6
7
8
9
10
11