kotlin coroutine withContext

这篇博客深入探讨了Kotlin协程中的withContext函数,它如何结合当前和指定的CoroutineContext执行挂起代码块,并在取消时抛出异常。文章详细解释了函数内部的逻辑,包括检查新context的取消、快速路径判断以及使用不同调度器的情况。此外,还强调了withContext调用的可取消性和其在原调度器中的取消行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


使用给出的coroutine context调用指定的挂起代码块block,挂起直到它完成并返回结果
对block产生的结果context来自`coroutineContext + context` (see [CoroutineContext.plus])操作合并当前的coroutineContext和指定的context。这个挂起函数是可取消的。它立即检查结果context的取消,如果它不是active[CoroutineContext.isActive].就抛出[CancellationException]异常。
这个函数使用来自新的context的派发器,如果一个新的派发器被指定,就把block的执行转移到不同的线程,并且完成时,返回原来的派发器。withContext的调用结果是以可取消的方式派发到原来的context,这意味着withContext调用所在的原来的coroutineContext在它的派发器开始执行这段代码的时候被取消,它会放弃withContext的结果并抛出异常

/**
 * Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns
 * the result.
 *
 * The resulting context for the [block] is derived by merging the current [coroutineContext] with the
 * specified [context] using `coroutineContext + context` (see [CoroutineContext.plus]).
 * This suspending function is cancellable. It immediately checks for cancellation of
 * the resulting context and throws [CancellationException] if it is not [active][CoroutineContext.isActive].
 *
 * This function uses dispatcher from the new context, shifting execution of the [block] into the
 * different thread if a new dispatcher is specified, and back to the original dispatcher
 * when it completes. Note that the result of `withContext` invocation is
 * dispatched into the original context in a cancellable way, which means that if the original [coroutineContext],
 * in which `withContext` was invoked, is cancelled by the time its dispatcher starts to execute the code,
 * it discards the result of `withContext` and throws [CancellationException].
 */

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
        val newContext = oldContext + context
        // always check for cancellation of new context
        newContext.checkCompletion()
        // FAST PATH #1 -- new context is the same as the old one
        if (newContext === oldContext) {
            val coroutine = ScopeCoroutine(newContext, uCont)
            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(newContext, null) {
                return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
            }
        }
        // SLOW PATH -- use new dispatcher
        val coroutine = DispatchedCoroutine(newContext, uCont)
        coroutine.initParentJob()
        block.startCoroutineCancellable(coroutine, coroutine)
        coroutine.getResult()
    }
}

### Kotlin 协程简介 Kotlin 协程提供了一种轻量级的线程模型,可以显著改善应用程序性能并简化异步编程逻辑[^2]。 ### 基本概念 协程通过挂起函数实现非阻塞操作。挂起函数可以在不阻塞线程的情况下暂停执行,并在稍后恢复。这使得编写异步代码更加直观和易于维护。 ### 创建与启动协程 要创建一个新的协程,通常会使用 `launch` 或者 `async` 函数: ```kotlin import kotlinx.coroutines.* fun main() { // 启动一个新协程 GlobalScope.launch { delay(1000L) // 非阻塞延迟一秒钟 (默认时间单位是毫秒) println("World!") } println("Hello,") // 主线程继续运行 Thread.sleep(2000L) // 阻塞主线程两秒以等待子协程完成 } ``` 这段代码展示了如何在一个新的协程中执行一段延时打印语句,而不会影响到主线程上的其他工作[^3]。 ### 使用挂起函数 下面是一个更复杂的例子,展示了一个挂起函数的实际应用: ```kotlin import kotlinx.coroutines.* import kotlin.system.measureTimeMillis suspend fun doNetworkCall(): String = withContext(Dispatchers.IO){ delay(1000L) // 模拟网络请求耗时 "Data from network" } fun main() = runBlocking{ val time = measureTimeMillis { val result = async { doNetworkCall() } // 异步发起网络调用 println(result.await()) // 等待结果返回后再处理 } println("Completed in $time ms") } ``` 此示例说明了如何利用 `withContext()` 来改变调度器上下文,在后台线程上执行 I/O 密集型任务;同时也演示了 `async/await` 的组合来并发地获取数据[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值