我在玩 Kotlin Coroutines,結果遇到了我不理解的情況。假設我有兩個suspend功能:
suspend fun coroutine() {
var num = 0
coroutineScope {
for (i in 1..1000) {
launch {
delay(10)
num = 1
}
}
}
println("coroutine: $num")
}
和:
suspend fun runBlocked() = runBlocking {
var num = 0
for (i in 1..1000) {
launch {
delay(10)
num = 1
}
}
println("Run blocking: $num")
}
然后我從main()方法中呼叫它們:
suspend fun main() {
coroutine()
runBlocked()
}
該coroutine()方法列印(如預期的那樣)一個幾乎從不為 1000 的數字(通常在 970 和 999 之間)。我明白為什么。
我不明白為什么runBlocked()函式總是列印 0。
協程:998
運行阻塞:0
我又試了一次,這次做了一個與 類似的函式runBlocked(),不同的是這次方法是回傳一個值而不是列印:
suspend fun runBlockedWithReturn(): Int = runBlocking {
var num = 0
for (i in 1..1000) {
launch {
delay(10)
num = 1
}
}
return@runBlocking num
}
然后我從main()方法中呼叫它:
suspend fun main() {
val result = runBlockedWithReturn()
println("Run blocking with return: $result")
}
...但該方法回傳 0。
這是為什么?以及如何修復runBlocked()列印接近 1000 而不是 0 的數字的方法?我錯過了什么?
uj5u.com熱心網友回復:
runBlocking一開始就不能從協程中呼叫。由于我們通過將其置于掛起函式中而違反了此合同,因此我們對它的行為方式的任何解釋可能在不同平臺或將來會有所不同。
除此之外,除非您在使用可以處理它的調度程式的 CoroutineContext 中,否則永遠不應在協程中呼叫阻塞代碼,例如Dispatchers.IO.
也就是說,發生這種情況的原因是coroutineScope暫停,直到其所有子協程完成,而runBlocking不是。您正在啟動協程runBlocking然后立即回傳,因此這些協程都沒有機會在您回傳時開始運行num從函式回傳時,這些協程都不一定有機會開始運行。
如果你想等待所有在runBlockinglambda 中啟動的協程,你需要join()每個協程。您可以將它們放在串列中并呼叫joinAll()它。例如:
(1..1000).map {
launch {
delay(10)
num = 1
}
}.joinAll()
但同樣,runBlocking永遠不應該在協程中呼叫。我只是在描述如果你runBlocking從協程外部使用它來實作它的預期目的,你將如何做到這一點,以在非協程和協程代碼之間架起一座橋梁。
uj5u.com熱心網友回復:
你永遠不應該runBlocking從suspend fun.
在任何情況下,在從 中回傳值之前,您都不會等待已啟動的協程完成runBlocking,但是您的使用會coroutineScope強制該函式中已啟動的協程在您回傳之前完成。
uj5u.com熱心網友回復:
現有的答案集中在我們不應該做的事情上,但我認為他們沒有抓住重點,所以我們看到差異的主要原因。
兩者都coroutineScope()保證runBlocking()在退出代碼塊后,里面的所有協程都已經運行完畢。但是由于某種原因,我不知道是不是故意的,你寫的兩個案例都不一樣。在coroutine()示例中,您將其放在塊println()下方coroutineScope(),因此可以保證在所有孩子之后運行。另一方面,在runBlocked()你放println()里面runBlocking(),所以它同時運行給孩子。runBlocked()只需以類似的方式重寫你的coroutine(),所以把它放在println()下面runBlocking(),你會看到1000,正如你所期望的那樣。
兩個示例之間的另一個區別是,默認情況下runBlocking()使用單個執行緒運行,而coroutineScope()可以使用其中的多個。由于這個原因coroutineScope(),會產生一個隨機值,這是不安全共享可變狀態的結果。runBlocking()更可預測。它總是產生0if println()is inside,因為println()在任何孩子之前運行。或者它總是產生1000if println()is below runBlocking(),因為孩子實際上一次運行一個,他們不會并行修改值。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/520363.html
標籤:多线程科特林kotlin 协程
