Vit*_*aly 5 counter progress coroutine kotlin kotlin-coroutines
我正在使用 async/await 发出数千个 HTTP 请求,并希望有一个进度指示器。我以一种天真的方式添加了一个,但注意到当所有请求都完成时,计数器值永远不会达到总数。所以我创建了一个简单的测试,果然,它没有按预期工作:
fun main(args: Array<String>) {
var i = 0
val range = (1..100000)
range.map {
launch {
++i
}
}
println("$i ${range.count()}")
}
Run Code Online (Sandbox Code Playgroud)
输出是这样的,第一个数字总是改变:
98800 100000
Run Code Online (Sandbox Code Playgroud)
我可能在 JVM/Kotlin 中遗漏了一些关于并发/同步的重要细节,但不知道从哪里开始。有小费吗?
更新:我最终按照 Marko 的建议使用了频道:
/**
* Asynchronously fetches stats for all symbols and sends a total number of requests
* to the `counter` channel each time a request completes. For example:
*
* val counterActor = actor<Int>(UI) {
* var counter = 0
* for (total in channel) {
* progressLabel.text = "${++counter} / $total"
* }
* }
*/
suspend fun getAssetStatsWithProgress(counter: SendChannel<Int>): Map<String, AssetStats> {
val symbolMap = getSymbols()?.let { it.map { it.symbol to it }.toMap() } ?: emptyMap()
val total = symbolMap.size
return symbolMap.map { async { getAssetStats(it.key) } }
.mapNotNull { it.await().also { counter.send(total) } }
.map { it.symbol to it }
.toMap()
}
Run Code Online (Sandbox Code Playgroud)
到底是什么导致你的错误方法失败的解释是次要的:首要的是修复方法。
对于这种通信模式,您应该使用 来代替async-await或,所有 HTTP 作业都会向其发送其状态。这将自动处理您所有的并发问题。launchactor
以下是一些示例代码,取自您在评论中提供的链接,并根据您的用例进行了调整。Actor 在 UI 上下文中运行并更新 GUI 本身,而不是第三方询问计数器值并用它更新 GUI:
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.channels.*
import kotlin.system.*
import kotlin.coroutines.experimental.*
object IncCounter
fun counterActor() = actor<IncCounter>(UI) {
var counter = 0
for (msg in channel) {
updateView(++counter)
}
}
fun main(args: Array<String>) = runBlocking {
val counter = counterActor()
massiveRun(CommonPool) {
counter.send(IncCounter)
}
counter.close()
println("View state: $viewState")
}
// Everything below is mock code that supports the example
// code above:
val UI = newSingleThreadContext("UI")
fun updateView(newVal: Int) {
viewState = newVal
}
var viewState = 0
suspend fun massiveRun(context: CoroutineContext, action: suspend () -> Unit) {
val numCoroutines = 1000
val repeatActionCount = 1000
val time = measureTimeMillis {
val jobs = List(numCoroutines) {
launch(context) {
repeat(repeatActionCount) { action() }
}
}
jobs.forEach { it.join() }
}
println("Completed ${numCoroutines * repeatActionCount} actions in $time ms")
}
Run Code Online (Sandbox Code Playgroud)
运行它打印
Completed 1000000 actions in 2189 ms
View state: 1000000
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5210 次 |
| 最近记录: |