Kotlin协程相关知识点学之一

本片文章主要学习协程相关的基础知识点


Hello 协程

协程语法简介

1
2
3
4
5
6
7
//主要语法 【在后台启动一个新的协程并继续】
GlobalScope.launch {
// 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
delay(1000L) //类似 Thread.sleep(1000L)
// 在延迟后打印输出
println("World!")
}
  • 通俗理解:
    • 可以将 GlobalScope.launch { …… } 替换为 thread { …… }
    • 将 delay(……) 替换为 Thread.sleep(……) 达到同样目的【但是,delay(……) 仅支持在GlobalScope.launch { …… }方法体内执行】
  • 示例如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import kotlinx.coroutines.*

    fun main() {
    //主要语法 【在后台启动一个新的协程并继续】
    GlobalScope.launch {
    // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
    delay(1000L)
    // 在延迟后打印输出
    println("World!")
    }
    // 协程已在等待时主线程还在继续
    println("Hello,")
    // 阻塞主线程 2 秒钟来保证 JVM 存活
    Thread.sleep(2000L)
    }
  • 输出结果:
    1
    2
    Hello,
    World!

桥接阻塞与非阻塞的世界

  • 阻塞
    1
    2
    3
    4
    5
    // 但是这个表达式阻塞了主线程
    runBlocking {
    // ……我们延迟 2 秒来保证 JVM 的存活
    delay(2000L)
    }
  • 非阻塞
    1
    2
    3
    4
    GlobalScope.launch { // 在后台启动一个新的协程并继续
    delay(1000L)
    println("World!")
    }
  • 嵌套协程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import kotlinx.coroutines.*

    fun main() = runBlocking<Unit> { // 开始执行主协程
    GlobalScope.launch { // 在后台启动一个新的协程并继续
    delay(1000L)
    println("World!")
    }
    println("Hello,") // 主协程在这里会立即执行
    delay(2000L) // 延迟 2 秒来保证 JVM 存活
    }

等待一个作业

主要语法

1
GlobalScope.launch {}.join()

示例:

1
2
3
4
5
6
7
val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
delay(1000L)
println("World!")
}
println("Hello,")
// 等待直到子协程执行结束
job.join()

结构化的并发

这里的知识点主要是避免:

  • 启动了太多协程导致内存不足
  • 手动保持对所有已启动协程的引用并 join 很容易出错

解决方案:
嵌套协程,我们只需要管理最外层的协程即可:

1
2
3
4
5
6
7
8
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch { // 在 runBlocking 作用域中启动一个新协程
delay(1000L)
println("World!")
}
println("Hello,")
}

作用域构建器

  • 关键字:
    1
    2
    // 创建一个协程作用域
    coroutineScope { }
  • 示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import kotlinx.coroutines.*

    fun main() = runBlocking { // this: CoroutineScope
    launch {
    delay(200L)
    //Task from runBlocking
    println("1")
    }

    // 创建一个协程作用域
    coroutineScope {
    launch {
    delay(500L)
    //Task from nested launch"
    println("2“)
    }
    delay(100L)
    // 这一行会在内嵌 launch 之前输出
    //Task from coroutine scope
    println("3")
    }
    // 这一行在内嵌 launch 执行完毕后才输出
    //Coroutine scope is over
    println("4")
    }
    输出:
    1
    1,3,2,4

提取函数重构

  • 关键字[fun前加suspend]
    1
    2
    suspend fun funName(){
    }
  • 示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import kotlinx.coroutines.*

    fun main() = runBlocking {
    launch { doWorld() }
    println("Hello,")
    }

    // 这是你的第一个挂起函数
    suspend fun doWorld() {
    delay(1000L)
    println("World!")
    }
    注意:在协程内部可以像普通函数一样使用挂起函数, 不过其额外特性是,同样可以使用其他挂起函数(如本例中的 delay)来挂起协程的执行。

协程很轻量

测试协程的轻量,启动了 10 万个协程,并且在一秒钟后,每个协程都输出一个点。

1
2
3
4
5
6
7
8
9
import kotlinx.coroutines.*
fun main() = runBlocking {
repeat(100_000) { // 启动大量的协程
launch {
delay(1000L)
print(".")
}
}
}

全局协程像守护线程

  • 关键字:

    1
    2
    3
    4
    repeat(1000) { i ->
    println("I'm sleeping $i ...")
    delay(500L)
    }

    注:repeat 取代for(int i=0;i<num;i++)用于简单的重复工作。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    GlobalScope.launch {
    repeat(1500) { i ->
    println("I'm sleeping $i ...")
    delay(500L)
    }
    }
    delay(1600L) // 在延迟后退出
  • 输出:

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

相关参考