Kotlin:withContext()vs Async await
我一直在阅读,如果我理解正确,两个Kotlin函数的工作原理如下:Kotlin:withContext()vs Async await,kotlin,kotlin-coroutines,Kotlin,Kotlin Coroutines,我一直在阅读,如果我理解正确,两个Kotlin函数的工作原理如下: withContext(context):切换当前协同路由的上下文,当给定块执行时,协同路由切换回上一个上下文 async(context):在给定的上下文中启动一个新的协程,如果我们对返回的Deferred任务调用.wait(),它将暂停调用协程,并在生成的协程内执行的块返回时恢复 下面是两个版本的code: 版本1: launch(){ block1() val returned = async(cont
withContext(context)
:切换当前协同路由的上下文,当给定块执行时,协同路由切换回上一个上下文async(context)
:在给定的上下文中启动一个新的协程,如果我们对返回的Deferred
任务调用.wait()
,它将暂停调用协程,并在生成的协程内执行的块返回时恢复code
:
版本1:
launch(){
block1()
val returned = async(context){
block2()
}.await()
block3()
}
launch(){
block1()
val returned = withContext(context){
block2()
}
block3()
}
版本2:
launch(){
block1()
val returned = async(context){
block2()
}.await()
block3()
}
launch(){
block1()
val returned = withContext(context){
block2()
}
block3()
}
与context
而不是async wait
是否总是更好,因为它在功能上类似,但不会创建另一个协同程序。大量的协同路由虽然轻量级,但在要求苛刻的应用程序中仍然可能是一个问题
async wait
比with context
更可取
async(ctx){}.await()转换为withContext(ctx){}
使用withContext不是总比使用AsynchAwait好,因为它在功能上类似,但不会创建另一个协同路由。大型numebrs协程,但在要求苛刻的应用程序中,轻量级可能仍然是一个问题
是否存在AsynchWait比withContext更可取的情况
要同时执行多个任务时,应使用async/await,例如:
runBlocking {
val deferredResults = arrayListOf<Deferred<String>>()
deferredResults += async {
delay(1, TimeUnit.SECONDS)
"1"
}
deferredResults += async {
delay(1, TimeUnit.SECONDS)
"2"
}
deferredResults += async {
delay(1, TimeUnit.SECONDS)
"3"
}
//wait for all results (at this point tasks are running)
val results = deferredResults.map { it.await() }
println(results)
}
runBlocking{
val delferredresults=arrayListOf()
延迟结果+=异步{
延迟(1,时间单位。秒)
"1"
}
延迟结果+=异步{
延迟(1,时间单位。秒)
"2"
}
延迟结果+=异步{
延迟(1,时间单位。秒)
"3"
}
//等待所有结果(此时任务正在运行)
val results=deferredResults.map{it.await()}
println(结果)
}
如果不需要同时运行多个任务,可以使用withContext
大量的协同路由虽然轻量级,但在要求苛刻的应用程序中仍然是一个问题
我想通过量化实际成本来消除“太多的合作项目”是一个问题的神话
首先,我们应该将协同程序本身从它所连接的协同程序上下文中分离出来。这就是如何以最小的开销创建协同程序:
GlobalScope.launch(Dispatchers.Unconfined) {
suspendCoroutine<Unit> {
continuations.add(it)
}
}
这段代码启动一系列协同程序,然后休眠,这样您就有时间使用VisualVM之类的监视工具分析堆。我创建了专门的类JobList
和ContinuationList
,因为这样更容易分析堆转储
为了获得更完整的故事,我还使用下面的代码来衡量
withContext()
和async await
的成本:
import kotlinx.coroutines.*
import java.util.concurrent.Executors
import kotlin.coroutines.suspendCoroutine
import kotlin.system.measureTimeMillis
const val JOBS_PER_BATCH = 100_000
var blackHoleCount = 0
val threadPool = Executors.newSingleThreadExecutor()!!
val ThreadPool = threadPool.asCoroutineDispatcher()
fun main(args: Array<String>) {
try {
measure("just launch", justLaunch)
measure("launch and withContext", launchAndWithContext)
measure("launch and async", launchAndAsync)
println("Black hole value: $blackHoleCount")
} finally {
threadPool.shutdown()
}
}
fun measure(name: String, block: (Int) -> Job) {
print("Measuring $name, warmup ")
(1..1_000_000).forEach { block(it).cancel() }
println("done.")
System.gc()
System.gc()
val tookOnAverage = (1..20).map { _ ->
System.gc()
System.gc()
var jobs: List<Job> = emptyList()
measureTimeMillis {
jobs = (1..JOBS_PER_BATCH).map(block)
}.also { _ ->
blackHoleCount += jobs.onEach { it.cancel() }.count()
}
}.average()
println("$name took ${tookOnAverage * 1_000_000 / JOBS_PER_BATCH} nanoseconds")
}
fun measureMemory(name:String, block: (Int) -> Job) {
println(name)
val jobs = (1..JOBS_PER_BATCH).map(block)
(1..500).forEach {
Thread.sleep(1000)
println(it)
}
println(jobs.onEach { it.cancel() }.filter { it.isActive})
}
val justLaunch: (i: Int) -> Job = {
GlobalScope.launch(Dispatchers.Unconfined) {
suspendCoroutine<Unit> {}
}
}
val launchAndWithContext: (i: Int) -> Job = {
GlobalScope.launch(Dispatchers.Unconfined) {
withContext(ThreadPool) {
suspendCoroutine<Unit> {}
}
}
}
val launchAndAsync: (i: Int) -> Job = {
GlobalScope.launch(Dispatchers.Unconfined) {
async(ThreadPool) {
suspendCoroutine<Unit> {}
}.await()
}
}
是的,async wait
的时间大约是withContext
的两倍,但仍然只有一微秒。你必须在一个紧密的循环中启动它们,除此之外几乎什么都不做,这样才能在你的应用程序中成为一个“问题”
使用measureMemory()
我发现每次调用的内存开销如下:
Just launch: 88 bytes
withContext(): 512 bytes
async-await: 652 bytes
async await
的成本比withContext
高出140字节,后者是我们得到的一个协程的内存重量。这只是设置CommonPool
上下文的全部成本的一小部分
如果性能/内存影响是决定withContext
和async await
之间的唯一标准,那么得出的结论是,在99%的实际用例中,它们之间没有相关差异
真正的原因是withContext()
是一种更简单、更直接的API,特别是在异常处理方面:
- 在
中未处理的异常会导致其父作业被取消。无论您如何处理匹配的async{…}
中的异常,都会发生这种情况。如果您没有为它准备await()
,它可能会导致整个应用程序宕机coroutineScope
- 在
中未处理的异常只会被withContext{…}
调用抛出,您可以像处理其他异常一样处理它withContext
withContext
也恰巧得到了优化,利用了暂停父协同路由并等待子协同路由的事实,但这只是一个额外的好处
async await
应该为那些您实际需要并发的情况保留,这样您就可以在后台启动多个协同路由,然后才等待它们。简言之:
-不要这样做,使用async await async await
withContext with context
-这就是使用它的方法async-async-await-await
- 如果多个任务必须并行执行,并且最终结果取决于所有任务的完成,则使用
async
- 要返回单个任务的结果,请使用
withContext
- 当有疑问时,请记住这一点,就像经验法则一样:
我认为,当您将
与上下文一起使用时,总是会创建一个新的协同程序。这是我从源代码中看到的。@stdout不async/await
还创建了一个新的协同程序,根据OP?关于的额外内存开销