协程范围和异步 - 正确的方法?
coroutine scope and async - right approach?
我喜欢协同例程的概念,并且一直在我的 android 项目中使用。目前我正在开发一个 JVM 模块,我将把它包含在一个 Ktor 项目中,我知道 ktor 支持协程。
(找到附加的代码片段)
只是想知道这是正确的方法吗?
我如何使用递归异步?
如果您能推荐任何可以帮助我掌握更深入的协程知识的资源。
提前致谢!
override suspend fun processInstruction(args.. ): List<Any> = coroutineScope {
val dataWithFields = async{
listOfFields.fold(mutableList()){ acc,field ->
val data = someProcess(field)
val nested = processInstruction(...nestedField) // nested call
acc.addAll(data)
acc.addAll(nested)
acc
}
}
return@coroutineScope postProcessData(dataWithFields.await())
}
如果你想并行处理所有嵌套调用,你应该将它们中的每一个都包装在 async
中(async
应该在循环内)。然后,在循环之后,您应该 await
所有结果。 (在您的代码中,您 运行 await
紧接在单个 async
之后,因此没有并行执行)。
例如,如果您有 Element
:
interface Element {
val subElements: List<Element>
suspend fun calculateData(): SomeData
}
interface SomeData
并且您想并行处理所有 subElements
中的 calculateData
,您可以这样做:
suspend fun Element.calculateAllData(): List<SomeData> = coroutineScope {
val data = async { calculateData() }
val subData = subElements.map { sub -> async { sub.calculateAllData() } }
return@coroutineScope listOf(data.await()) + subData.awaitAll().flatten()
}
正如你在评论区所说,需要父数据来计算子数据,因此calculateAllData()
首先要做的是计算父数据:
suspend fun Element.calculateAllData(
parentData: SomeData = defaultParentData()
): List<SomeData> = coroutineScope {
val data = calculateData(parentData)
val subData = subElements.map { sub -> async { sub.calculateAllData(data) } }
return@coroutineScope listOf(data) + subData.awaitAll().flatten()
}
现在您可能想知道它的工作速度有多快。考虑以下 Element
实施:
class ElementImpl(override val subElements: List<Element>) : Element {
override suspend fun calculateData(parentData: SomeData): SomeData {
delay(1000)
return SomeData()
}
}
fun elmOf(vararg elements: Element) = ElementImpl(listOf(*elements))
以及以下测试:
println(measureTime {
elmOf(
elmOf(),
elmOf(
elmOf(),
elmOf(
elmOf(),
elmOf(),
elmOf()
)
),
elmOf(
elmOf(),
elmOf()
),
elmOf()
).calculateAllData()
})
如果不需要parent-data来计算子数据,它会打印1.06s
,因为在这种情况下,所有数据都是并行计算的。否则,它打印 4.15s
,因为元素树的高度是 4.
我喜欢协同例程的概念,并且一直在我的 android 项目中使用。目前我正在开发一个 JVM 模块,我将把它包含在一个 Ktor 项目中,我知道 ktor 支持协程。
(找到附加的代码片段)
只是想知道这是正确的方法吗? 我如何使用递归异步?
如果您能推荐任何可以帮助我掌握更深入的协程知识的资源。
提前致谢!
override suspend fun processInstruction(args.. ): List<Any> = coroutineScope {
val dataWithFields = async{
listOfFields.fold(mutableList()){ acc,field ->
val data = someProcess(field)
val nested = processInstruction(...nestedField) // nested call
acc.addAll(data)
acc.addAll(nested)
acc
}
}
return@coroutineScope postProcessData(dataWithFields.await())
}
如果你想并行处理所有嵌套调用,你应该将它们中的每一个都包装在 async
中(async
应该在循环内)。然后,在循环之后,您应该 await
所有结果。 (在您的代码中,您 运行 await
紧接在单个 async
之后,因此没有并行执行)。
例如,如果您有 Element
:
interface Element {
val subElements: List<Element>
suspend fun calculateData(): SomeData
}
interface SomeData
并且您想并行处理所有 subElements
中的 calculateData
,您可以这样做:
suspend fun Element.calculateAllData(): List<SomeData> = coroutineScope {
val data = async { calculateData() }
val subData = subElements.map { sub -> async { sub.calculateAllData() } }
return@coroutineScope listOf(data.await()) + subData.awaitAll().flatten()
}
正如你在评论区所说,需要父数据来计算子数据,因此calculateAllData()
首先要做的是计算父数据:
suspend fun Element.calculateAllData(
parentData: SomeData = defaultParentData()
): List<SomeData> = coroutineScope {
val data = calculateData(parentData)
val subData = subElements.map { sub -> async { sub.calculateAllData(data) } }
return@coroutineScope listOf(data) + subData.awaitAll().flatten()
}
现在您可能想知道它的工作速度有多快。考虑以下 Element
实施:
class ElementImpl(override val subElements: List<Element>) : Element {
override suspend fun calculateData(parentData: SomeData): SomeData {
delay(1000)
return SomeData()
}
}
fun elmOf(vararg elements: Element) = ElementImpl(listOf(*elements))
以及以下测试:
println(measureTime {
elmOf(
elmOf(),
elmOf(
elmOf(),
elmOf(
elmOf(),
elmOf(),
elmOf()
)
),
elmOf(
elmOf(),
elmOf()
),
elmOf()
).calculateAllData()
})
如果不需要parent-data来计算子数据,它会打印1.06s
,因为在这种情况下,所有数据都是并行计算的。否则,它打印 4.15s
,因为元素树的高度是 4.