在使用协程前,必须引入相关的依赖包

1
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1")

协程(1)

本质上,协程可以称为轻量级线程
而且协程只能在协程作用域内才能启动。有CoroutineScope,GlobalScope(全局)两种作用域。而且在全局作用域内启动的协程,只受整个应用的生命周期的限制。即只要整个应用程序还在运行中,只要协程的任务还未结束,该协程就可以一直运行

1
2
3
4
delay()
//delay是一个挂起函数,他并不会阻塞线程,而是将协程挂起,在特定的时候再继续执行。
thread.sleep()
//而thread.sleep将会阻塞线程。

举例:当协程 A 调用 delay(1000L) 函数来指定延迟1秒后再运行时,协程 A 所在的线程只是会挂起,转而去执行协程 B,等到1秒后再把协程 A 加入到可调度队列里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//例程
fun main() = runBlocking {

repeat(100){
launch {
println(it)
}
}
// Thread.sleep(2000)
delay(2000)
println("你再干嘛")
}
//使用Thread时,前面所有的数据都不会输出,因为线程被阻塞在主线程中,而如果使用delay。会先重复完100次,再来输出最后的话

桥接阻塞与非阻塞的世界

1
2
3
4
5
6
7
8
9
10
fun main() { 
GlobalScope.launch { // launch a 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
}
}

在上述代码中,runBocking等同于一个普通函数,但是,它可以构造一个协程作用域,在该作用域内,可以拉起线程。而且他必须执行完内部的所有程序才会结束。
而他可以直接简化为main函数

1
2
3
4
5
6
7
8
fun main() = runBlocking<Unit> { // start main coroutine
GlobalScope.launch { // launch a 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
}

结构化并发

如果在同一个协程作用域内,那么只有该协程作用域内所有的协程都跑完才能结束这整个方法。(GlobalScope就不是该协程作用域)因此如果需要GlobalScope中拉起的协程不会提前终止,那么就需要将该拉起的协程加入到协程作用域内。

1
2
3
4
5
6
7
8
9
10
fun main() = runBlocking {
//sampleStart
val job = GlobalScope.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
//sampleEnd
}

作用域构建器

runBlocking 和 coroutineScope 看起来很像,因为它们都需要等待其内部所有相同作用域的子协程结束后才会结束自己。两者的主要区别在于 runBlocking 方法会阻塞当前线程,而 coroutineScope 只是挂起并释放底层线程以供其它协程使用。由于这个差别,所以 runBlocking 是一个普通函数,而 coroutineScope 是一个挂起函数,挂起函数必须得在其他的协程作用域内才能调用。
launch 函数是 CoroutineScope 的扩展函数,而 runBlocking 的函数体参数也是被声明为 CoroutineScope 的扩展函数,所以 launch 函数就隐式持有了和 runBlocking 相同的协程作用域。

挂起函数是可以被提取出来的

1
2
3
4
5
6
7
8
9
10
fun main() = runBlocking {
launch { doWorld() }
println("Hello,")
}

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

只需要加上suspend修饰符即可。

全局协程类似于守护线程

因为当主程序终止时,他也会跟着终止。