Reputation: 13
I have a kotlin function just as following, And I expected it can wrap a sync IO action into async.
suspend fun <T> runIOAsync(f:suspend () -> T): Deferred<T> = coroutineScope{
async(Dispatchers.IO) {
f()
}
}
Then I have my code in the calling side like
runBlocking {
repeat(5) {
runIOAsync {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}.await()
}
}
But the actual out put is
0
Thread[DefaultDispatcher-worker-1 @coroutine#2,5,main]
After sleep 0
1
Thread[DefaultDispatcher-worker-1 @coroutine#3,5,main]
After sleep 1
2
Thread[DefaultDispatcher-worker-1 @coroutine#4,5,main]
After sleep 2
3
Thread[DefaultDispatcher-worker-1 @coroutine#5,5,main]
After sleep 3
4
Thread[DefaultDispatcher-worker-1 @coroutine#6,5,main]
After sleep 4
Which seems all tasks from my function are executed serially. Any one can please help to give an explanation
Upvotes: 1
Views: 1604
Reputation: 37650
Let's put aside runIOAsync
for the moment.
You're using await()
right after calling async()
, which means that you're effectively waiting for the end of your async execution before executing the next one.
Instead, start all tasks first and then await all of them. For instance you can use awaitAll:
runBlocking {
List(5) {
async(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}.awaitAll()
}
Also, the way you're encapsulating the scope in runIOAsync
is wrong, you will be waiting for the end of the async execution even without calling await()
(coroutineScope
is a suspending function that waits for all its child coroutines before resuming).
Instead, use coroutineScope
to define the boundary of your coroutines executions, and you don't even have to await
them. Since you don't need to get values from this code, you can also use launch
instead of async
here:
coroutineScope {
repeat(5) {
launch(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}
}
Declaring a suspend
function returning a Deferred
should be a red flag, and is quite confusing from an API standpoint: if you suspend it means you wait, if you return Deferred
it means you don't wait (you immediately return a handle to some running computation). A function that does both would be quite weird.
If what you want is to make suspending code from IO-bound code, you can use instead the existing withContext function:
// this suspends until the code inside is done
withContext(Dispatchers.IO) {
// run some blocking IO code
}
However, note that this is independent from defining concurrent code. If you want to run multiple things concurrently, even with suspend functions, you'll need coroutine builders such as async
or launch
like in the above code.
Upvotes: 6