Reputation: 41
I thought I was familiar enough with Kotlin's coroutines, until I got this code.
1 to 8 are all printed except 2:
import kotlinx.coroutines.*
import java.lang.Runnable
import java.lang.Thread.sleep
import kotlin.concurrent.thread
fun main() {
runBlocking {
Client.createAccount()
delay(1000)
}
}
object Client: CoroutineScope {
override val coroutineContext = newSingleThreadContext("Client")
fun createAccount() = launch {
Client2.init(Runnable {
println('1')
launch {
println('2')
}
ok()
ok2()
})
println('7')
launch {
println('8')
}
}
fun ok() {
println('3')
launch {
println('4')
}
}
fun ok2() = launch {
println('5')
launch {
println('6')
}
}
}
object Client2 {
fun init(runnable: Runnable) = thread {
sleep(100)
runnable.run()
}
}
The result is:
7
8
1
3
4
5
6
The coroutine in callback will never be called. Why?
And if I remove the launch
in createAccount()
the 1 to 8 will be all printed.
Also if I use GlobalScope.launch { println('2') }
instead of launch { println('2') }
, I can also get the 2 printed.
Upvotes: 3
Views: 892
Reputation: 392
the reason is that anonymous class uses its wrapper scope as a parent.
launch { println('2') }
in Runnable { }
will be cancelled when parent job createAccount()
launched is completed.
Therefore, it can't be invoked because it would be cancelled right after launch { println('8') }
.
So, If you change Client
like below, it would print '2' correctly.
object Client: CoroutineScope {
override val coroutineContext = Dispatchers.Main
fun createAccount() = launch {
Client2.init(Run())
println("7")
launch {
println("8")
}
}
fun ok() {
println("3")
launch {
println("4")
}
}
fun ok2() = launch {
println("5")
launch {
println("6")
}
}
class Run: Runnable {
override fun run() {
println("1")
launch {
println("2")
}
ok()
ok2()
}
}
}
Upvotes: 3
Reputation: 5707
launch posts a Runnable in a Handler, so its code execution is not immediate. launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED) will immediately execute its lambda expression in the current thread.
change the dispatcher to the current one you are using change the lunch from inside the thread to
launch (coroutineContext, CoroutineStart.UNDISPATCHED)
.
fun createAccount() = launch {
Client2.init(Runnable {
println('1')
launch (coroutineContext, CoroutineStart.UNDISPATCHED){
println('2')
}
ok()
ok2()
})
println('7')
launch {
println('8')
}
}
output:
7
8
1
2
3
4
5
6
Upvotes: 0