kotlin coroutines,coroutineScope和withContext有什么区别

lan*_*nyf 8 kotlin-coroutines coroutinescope withcontext

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.
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)

withContext采用CoroutineContext,并且似乎都complete在其所有子级完成之后。

在什么情况下,withContext或者coroutineScope应该比其他的首选?

例如:

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

也可能是

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

两者都processAllPages()一样吗?


更新:请参见为什么withContext等待子协程的完成中的讨论

Mar*_*nik 5

正式地,这coroutineScopewithContext您在当前上下文中进行传递的特殊情况,避免了任何上下文切换。从示意图上来说,

coroutineScope ? withContext(this.coroutineContext)
Run Code Online (Sandbox Code Playgroud)

由于切换上下文只是的几个功能之一withContext,所以这是一个合法的用例。withContext等待您在块中启动的所有协程完成。如果它们中的任何一个失败,则两者都将自动取消所有其他协程,并且整个块将引发异常,但不会自动取消您从中调用它们的协程。

每当需要这些功能而无需切换上下文时,都应该始终选择coroutineScope它,因为它可以更清楚地表明您的意图。

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

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

  • @FRR 仅在您调用“withContext”而不实际切换上下文的特殊情况下,它们在语义上是相同的,这显然违背了其预期用途。 (2认同)
  • @DanieleSegato 查看[这个问题](/sf/ask/3983284021/)。在早期(实验性?)版本中它不会等待,但自从引入结构化并发以来,它会等待。 (2认同)