Kotlin协程-协程的不同启动方式
2025/4/30大约 3 分钟AndroidKotlinKotlin Coroutines
协程的不同启动方式
runBlocking
在普通函数中启动顶级协程,阻塞当前线程直到协程体执行完毕。
代码探究
@Throws(InterruptedException::class)
public actual fun <T> runBlocking(context: CoroutineContext, block: suspend CoroutineScope.() -> T): T {
// ....
val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
return coroutine.joinBlocking()
}
// BlockingCoroutine
@Suppress("UNCHECKED_CAST")
fun joinBlocking(): T {
registerTimeLoopThread()
try {
eventLoop?.incrementUseCount()
try {
while (true) {
@Suppress("DEPRECATION")
if (Thread.interrupted()) throw InterruptedException().also { cancelCoroutine(it) }
val parkNanos = eventLoop?.processNextEvent() ?: Long.MAX_VALUE
// note: process next even may loose unpark flag, so check if completed before parking
if (isCompleted) break
parkNanos(this, parkNanos)
}
} finally { // paranoia
eventLoop?.decrementUseCount()
}
} finally { // paranoia
unregisterTimeLoopThread()
}
// now return result
val state = this.state.unboxState()
(state as? CompletedExceptionally)?.let { throw it.cause }
return state as T
}
}
不过多深入了,若需要深入请看前文,这里核心也就是通过BlockingCoroutine启动了block的SuspendLambda,并在while循环中处理eventLoop中的消息以及进行park操作。
withContext
在指定Dispatcher调度器切换上下问并运行代码,挂起当前协程直到完成内部协程部分代码后才进行后续代码.
实质是启动了DispatchedCoroutine
/UndispatchedCoroutine
来执行block块
- 不创建额外的Job
- 只挂起当前协程,并在目标调度器上执行代码,结束后恢复原协程
- 不像launch/async那样并发地启动多个执行单元
public suspend fun <T> withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// compute new context
val oldContext = uCont.context
// Copy CopyableThreadContextElement if necessary
val newContext = oldContext.newCoroutineContext(context)
// always check for cancellation of new context
newContext.ensureActive()
// FAST PATH #1 -- new context is the same as the old one
if (newContext === oldContext) {
val coroutine = ScopeCoroutine(newContext, uCont)
@Suppress("LEAKED_IN_PLACE_LAMBDA") // Contract is preserved, invoked immediately or throws
return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
}
// FAST PATH #2 -- the new dispatcher is the same as the old one (something else changed)
// `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
val coroutine = UndispatchedCoroutine(newContext, uCont)
// There are changes in the context, so this thread needs to be updated
withCoroutineContext(coroutine.context, null) {
@Suppress("LEAKED_IN_PLACE_LAMBDA") // Contract is preserved, invoked immediately or throws
return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
// SLOW PATH -- use new dispatcher
val coroutine = DispatchedCoroutine(newContext, uCont)
@Suppress("LEAKED_IN_PLACE_LAMBDA") // Contract is preserved, invoked immediately or throws
block.startCoroutineCancellable(coroutine, coroutine)
coroutine.getResult()
}
}
举个例子
对于以下代码
fun main() {
runBlocking(Dispatchers.Default) {
withContext(Dispatchers.Default) {
println("start - 1")
delay(3000)
println("end - 1")
}
withContext(Dispatchers.Default) {
println("start - 2")
delay(3000)
println("end - 2")
}
}
}
是先彻底执行完第一个withContext的代码后再执行第二部分,log为
start - 1
end - 1
start - 2
end - 2
launch
返回Job, 启动一个“火车”协程,不关心结果,只管执行副作用(打印、网络请求、UI更新等)。
不会阻塞调用者,协程失败时默认打印异常但不会影响其他协程
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
}
本质是启动了StandaloneCoroutine协程
Async
返回一个Deferred<T>, 即启动一个有返回值的协程,结果有await()挂起等待
适合并行计算,以及需要拿到结果再继续后续的逻辑的场景
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
底层是基于DeferredCoroutine进行
coroutineScope / supervisorScope
创建一个新的协程作用域,其中启动的字写成会被结构化管理,直到全部完成或某个失败时才退出作用域
- coroutineScope中若任何子协程失败,则会取消整个作用域
- supervisorScope则不会因为单个子协程失败而取消其他子协程
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)
@Suppress("LEAKED_IN_PLACE_LAMBDA") // Contract is preserved, invoked immediately or throws
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = SupervisorCoroutine(uCont.context, uCont)
@Suppress("LEAKED_IN_PLACE_LAMBDA") // Contract is preserved, invoked immediately or throws
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
继承关系一览
