kotlin协程 coroutineScope 和 runBlocking

时间:2022-08-13 01:04:50

coroutineScope是一个suspend函数,创建一个新的协程作用域,并在该作用域内执行指定代码块,它并不启动协程。其存在的目的是进行符合结构化并发的并行分解(将长耗时任务拆分为并发的多个短耗时任务,并等待所有并发任务完成后再返回)。

public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return suspendCoroutineUninterceptedOrReturn { uCont ->
        val coroutine = ScopeCoroutine(uCont.context, uCont)
        coroutine.startUndispatchedOrReturn(coroutine, block)
    }
}

coroutineScope是一个挂起函数,每一个被suspend修饰的方法都必须在另一个suspend函数或者Coroutine协程程序中进行调用

CoroutineScope(Dispatchers.Main).launch{
    coroutineScope {
        //...       
    }
}

coroutineScope与runBlocking的区别在于:

runBlocking会阻塞当前线程,而coroutineScope不会阻塞所在的线程,它会挂起所在的协程直至其内部任务(包括子协程)执行完成。

private fun main() = runBlocking {
    launch { // launch1
        println("test2")
        delay(1000) // delay1 // 挂起launch1
        println("test4")
    }
    println("test1")
    coroutineScope { // 第一次挂起runBlocking,直至内部逻辑完成
        launch { // launch2
            delay(2000) // delay2 // 挂起launch2
            println("test5")
        }
        delay(500) // delay3 // 第二次挂起runBlocking
        println("test3")
    }
    println("test6")
}
//System.out:
//test1
//test2
//test3
//test4
//test5
//test6

上述代码分析:

  1. runBlocking在main线程创建并启动一个阻塞的协程,会阻塞main线程;

  1. 创建launch1子协程,由于创建协程是需要一些时间的,并且协程的创建是由特定的线程来完成,并非是main线程。所以在创建协程过程中会并行地执行后续代码。因此test1被输出。

  1. 执行到coroutineScope函数时,把runBlocking挂起,直到内部逻辑执行完成,所以最后输出test6。

  1. 然后创建launch2协程,创建过程中执行执行后续代码:delay3继续挂起runBlocking500ms(挂起函数中调用挂起函数)。

  1. 等到launch1创建完毕时,输出test2,delay1把launch1挂起1s。launch2创建完毕时,delay2把launch2挂起2s。

  1. 此时runBlocking、launch1、launch2都是被挂起状态。

  1. 等到500ms后runBlocking第二次挂起被恢复,输出test3,1s后launch1恢复,输出test4;2s后launch2被恢复,输出test5。

  1. 此时coroutineScope中的逻辑已经执行完成,恢复runBlocking的第一次挂起,test6被输出。

kotlin协程 coroutineScope 和  runBlocking

使用了runBlocking函数,导致主线程阻塞:

launch1和coroutineScope是并行的,launch1挂起时间delay1 == 1s;coroutineScope内部launch2和delay3是并行的,launch2挂起时间delay2 == 2s,delay3 == 500ms,故coroutineScope挂起时间 == 2s,coroutineScope挂起时间 > launch1挂起时间,所以runBlocking函数的阻塞时间 == 2s。