【问题标题】:kotlin coroutines, what is the difference between coroutineScope and withContextkotlin协程,coroutineScope和withContext有什么区别
【发布时间】:2019-11-13 11:09:50
【问题描述】:
withContext
suspend fun <T> withContext(
    context: CoroutineContext, 
    block: suspend CoroutineScope.() -> T
): T (source)
Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns the result.
suspend fun <R> coroutineScope(
    block: suspend CoroutineScope.() -> R
): R (source)
Creates a CoroutineScope and calls the specified suspend block with this scope. The provided scope inherits its coroutineContext from the outer scope, but overrides the context’s Job.

withContext 采用 CoroutineContext,在其所有子代都完成后,两者似乎都是 complete

在什么情况下withContextcoroutineScope 应该比另一个更受欢迎?

例如:

suspend fun processAllPages() = withContext(Dispatchers.IO) { 
    // withContext waits for all children coroutines 
    launch { processPages(urls, collection) }
    launch { processPages(urls, collection2) }
    launch { processPages(urls, collection3) }
}

也可以

suspend fun processAllPages() = coroutineScope { 
    // coroutineScope waits for all children coroutines 
    launch { processPages(urls, collection) }
    launch { processPages(urls, collection2) }
    launch { processPages(urls, collection3) }
}

processAllPages() 都在做同样的事情吗?


更新:见Why does withContext await for the completion of child coroutines讨论

【问题讨论】:

    标签: kotlin-coroutines coroutinescope withcontext


    【解决方案1】:

    形式上,coroutineScopewithContext 的一个特例,您在其中传递当前上下文,避免任何上下文切换。从示意图上讲,

    coroutineScope ≡ withContext(this.coroutineContext)
    

    由于切换上下文只是withContext 的几个功能之一,因此这是一个合法的用例。 withContext 等待您在块内启动的所有协程完成。如果其中任何一个失败,它将自动取消所有其他协程,整个块将抛出异常,但不会自动取消您调用它的协程。

    当您需要这些功能而不需要切换上下文时,您应该始终首选coroutineScope,因为它更清楚地表明了您的意图。

    coroutineScope 是关于几个子协程的作用域生命周期。它用于将一个任务分解为几个并发的子任务。您不能使用它更改上下文,因此它从当前上下文继承 Dispatcher。通常每个子协程会在需要时指定不同的Dispatcher

    withContext 通常不用于启动子协程,而是临时切换当前协程的上下文。它应该在其代码块完成后立即完成(从版本 1.3.2 开始,这实际上仍在其文档中说明)。它的主要用例是将长操作从事件循环线程(例如主 GUI 线程)卸载到使用自己的线程池的Dispatcher。另一个用例是定义一个“关键部分”,在该部分中协程不会对取消请求做出反应。

    【讨论】:

    • 感谢@Marko Topolnik!当您对coroutineScope() 说“它用于将一个任务分解为多个并发子任务。”时,这是否也适用于withContext()?除此之外,withContext() 可以指定上下文,它们都挂起,直到所有子协程完成。如果上下文无关紧要,似乎它们都可以在这些地方使用,不是吗?对于withContext(),它还提到"This suspending function is cancellable.",是否意味着如果父/调用被取消,它将被取消?我猜在withContext() 一个子协程失败不会取消它的兄弟姐妹。
    • @FRR 仅在您调用withContext 而不实际切换上下文的特殊情况下,它们在语义上是相同的,这显然违背了它的预期用途。
    • @DanieleSegato 查看this question。在早期(实验性?)版本中,它不会等待,但自从引入结构化并发后,它就可以了。
    • 你是对的,看看我最初的代码虽然他们没有做同样的事情,但第二次看(更仔细地)他们都在相同上下文的情况下使用ScopedCoroutine .对不起,误导性评论。
    • @AbhijitSarkar 很可能有,但它仍然没有意义。为什么会有“在当前上下文中继续执行”的内容?范围部分完全不明显,实际上withContext 甚至不用于并行分解的用例。该部分在以后的迭代中对其进行了改装。
    猜你喜欢
    • 2021-01-02
    • 2021-08-26
    • 2020-04-09
    • 1970-01-01
    • 2022-11-12
    • 1970-01-01
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多