Pihentagy
06/13/2023, 2:50 PMkotlinx.coroutines.async
?
I have now:
dependencies {
implementation ("org.jetbrains.kotlin:kotlin-stdlib")
implementation ("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutinesVersion")
Joffrey
06/13/2023, 2:51 PMkotlinx-coroutines-core
is enoughJoffrey
06/13/2023, 2:52 PMkotlin-stdlib
Joffrey
06/13/2023, 2:53 PMkotlinx-coroutines-jdk8
dependency (which was merged with the core)Joffrey
06/13/2023, 2:54 PMkotlin-reflect
, it depends on whether something else in your project needs itPihentagy
06/13/2023, 2:58 PMUnresolved reference: async
for the line
val auctionAssetInfoDeferred = kotlinx.coroutines.async {
when I do a gradle clean build
Pihentagy
06/13/2023, 3:01 PMval coroutinesVersion = "1.7.0"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
Joffrey
06/13/2023, 3:05 PMasync
cannot be used this way, though. If you check the documentation of this function, you'll see that it requires a CoroutineScope
as receiver.
You need to add an import kotlinx.coroutines.async
at the top of your file, and then just use async { ... }
(unprefixed) in a place where a CoroutineScope
is in context, or scope.async { ... }
where scope
is a CoroutineScope
.
Using a fully qualified name with the kotlinx.coroutines.
prefix doesn't work if the function expects a receiver (I guess that's a syntax limitation).Pihentagy
06/13/2023, 3:11 PMJoffrey
06/13/2023, 3:13 PMsuspend fun runTasksInParallel(): SomeResult = coroutineScope {
val deferredResult1 = async { task1() }
val deferredResult2 = async { task2() }
SomeResult(deferredResult1.await(), deferredResult2.await())
}
Or if you're not using coroutines anywhere else and you want a blocking call rather than a suspend function:
fun runTasksInParallel(): SomeResult = runBlocking(Dispatchers.Default) {
val deferredResult1 = async { task1() }
val deferredResult2 = async { task2() }
SomeResult(deferredResult1.await(), deferredResult2.await())
}
Or use launch
instead of async
if you don't need the results:
fun runTasksInParallel() = runBlocking(Dispatchers.Default) {
launch { task1() }
launch { task2() }
}
Pihentagy
06/13/2023, 3:16 PMJoffrey
06/13/2023, 3:18 PM<http://Dispatchers.IO|Dispatchers.IO>
Pihentagy
06/13/2023, 3:20 PMJoffrey
06/13/2023, 3:25 PMGlobalScope.async { ... }
and deferred.await()
in Kotlin (I had written this comparison a while ago if that helps you).
Note that using GlobalScope
is regarded as a bad idea in Kotlin because of this problem. It escapes the safety of structured concurrency. You basically start coroutines flying around without making sure they don't hang (and thus leak) forever, or this kind of things.Joffrey
06/13/2023, 3:27 PMcoroutineScope { ... }
to give some sort of lifetime to the async work we startPihentagy
06/13/2023, 3:29 PMfun runTasksInParallel(): SomeResult = runBlocking(Dispatchers.Default) {
val deferredResult1 = async { task1() }
val deferredResult2 = async { task2() }
SomeResult(deferredResult1.await(), deferredResult2.await())
}
Joffrey
06/13/2023, 3:34 PMSo having a GlobalScope is something like a global lock?Not really, it doesn't help synchronizing anything actually. It's more of a "no scope" equivalent. A scope in Kotlin coroutines can be seen as a way to delimit the lifetime of the coroutines that you launch in it (it acts as a parent, and allows to cancel all child tasks in bulk for instance). The
GlobalScope
doesn't act as a parent, it doesn't keep track of children, it doesn't allow to cancel children in bulk. It's sort of an escape hatch for very narrow use cases.
So, to just parallelize one function, your second approach is a good startYeah that is a good start.
runBlocking
basically blocks the calling thread while waiting for all child coroutines to finish, and provides a coroutine scope that delimits their lifetime. You know that, when this runBlocking
call returns, all child coroutines have completed in some way (and thus cannot leak).
coroutineScope
is the equivalent of runBlocking
, but it suspends the calling coroutine instead of blocking the calling thread. So it requires you to be already inside the coroutines world (because it's marked as suspend
itself).Joffrey
06/13/2023, 3:36 PMPihentagy
06/13/2023, 3:49 PMJoffrey
06/13/2023, 3:50 PMJeff Lockhart
06/13/2023, 4:07 PMasync
without a CoroutineScope
receiver. This was before the API became stable and requires this to enforce structure concurrency. So if you really have a use case to have your async work run with an unbounded scope, you explicitly use GlobalScope
to declare this intent.
I recommend reading Roman's blog post about structured concurrency. He has many good posts on the topic of coroutines: https://elizarov.medium.com/Jeff Lockhart
06/13/2023, 4:12 PM