Kotlin协程(1)

协程与线程

协程是轻量级的线程,线程调度由系统负责。协程只是编译级别的,调度由编译者负责,协程运行在线程上,一个线程可以开启成千上万个协程。

第一个协程程序

fun main(args: Array<String>) {

    GlobalScope.launch { // 在主线程上启动一个协程,并执行之后的代码
        delay(1000L) // 非阻塞式延迟1s
        println("world!")//延迟结束后打印
    }

    println("Hello") // 主线程继续执行, 不受协程影响

    Thread.sleep(2000L)//阻塞式延迟2s,保证主线程存活
}

协程在 CoroutineScope (协程作用域)的上下文中通过 launchasync 等协程构造器(coroutine builder)来启动。GlobalScope是全局作用域内启动协程,该协程的生命周期只受整个应用程序的生命周期的限制,即只要整个应用程序还在运行中,只要协程的任务还未结束,该协程就可以一直运行。delay() 是一个挂起函数(suspending function),挂起函数只能由协程或者其它挂起函数进行调度。挂起函数不会阻塞线程,而是会将协程挂起,在特定的时候才再继续运行。

fun main(args: Array<String>) {

    thread {
        GlobalScope.launch {
            delay(4000L)
            println("world!")
        }
        println("Hello")
        Thread.sleep(2500)
        println("continue")
    }

    Thread.sleep(5000L)
}
// 结果
Hello
continue
world!

阻塞与非阻塞

在线程中调用sleep线程被阻塞,后面的动作将不会执行。协程作用域内使用delay为非阻塞,改协程被挂起,线程转而执行其他懂得,一段时间后将协程放入可执行队列开始执行。同样的协程中有runBlocking,只有其内部的所有协程执行完后才会执行之后 的动作。

fun main(args: Array<String>) {


    GlobalScope.launch {
        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
    } 
}

延迟等待不是好的选择。可以显式(非阻塞的方式)地等待协程执行完成。这里main被定义为主协程。需要等待其内部所有协程执行完才可执行。

@OptIn(DelicateCoroutinesApi::class)
fun main(args: Array<String>) = runBlocking{
    
    val job = GlobalScope.launch { 
        delay(1000)
        println("world!")
    }
    println("Hello")
    job.join()
}

结构化并发

GlobalScope.launch 会创建一个顶级协程,只有程序结束或者其内部的动作执行完才会结束。有时候也会浪费内存,需要可以在特定的范围内来启动协程。协程构造器(包括 runBlocking)都会将 CoroutineScope 的实例添加到其代码块的作用域中。可以在这个作用域中启动协程,因为外部协程,在其作用域中启动的所有协程完成之前不会结束。

Launch函数是·CoroutineScope的扩展函数

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

作用域构建器

使用 coroutineScope 来声明自己的作用域。coroutineScope 用于创建一个协程作用域,直到所有启动的子协程都完成后才结束。 runBlocking 方法会阻塞当前线程,而 coroutineScope 只是挂起并释放底层线程以供其它协程使用。

fun main(args: Array<String>) = runBlocking{

    launch {
        delay(200)
        println("Task from runBlocking")
    }

    coroutineScope {
        launch {
            delay(500)
            println("Task from coroutineScope inner")
        }

        delay(100)
        println("Task from coroutine scope")
    }

    println("Coroutine scope is over")
}

Task from coroutine scope
Task from runBlocking
Task from coroutineScope inner
Coroutine scope is over

延迟100ms后打印Task from coroutine scope,转而执行runBlocking开启的协程,延迟200ms打印Task from runBlocking,延迟500ms后打印Task from coroutineScope inner。所有协程执行完打印Coroutine scope is over。可见runBlocking会阻塞当前线程,而coroutineScope 只是挂起协程转而执行其他。

提取函数并重构

抽取 launch 内部的代码块为一个独立的函数,需要将之声明为挂起函数。挂起函数可以像常规函数一样在协程中使用,但它们的额外特性是:可以依次使用其它挂起函数(如 delay 函数)来使协程挂起。

fun main(args: Array<String>) = runBlocking{

    launch {
        printWorld()
    }
    println("hello")

}

suspend fun printWorld() {
    delay(1000)
    println("world")
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值