Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin parallel coroutines

Is it acceptable to save multiple job instances from separate coroutines. Let's say i want to run a couple coroutines at once in which they are unrelated and cannot happen in one coroutine but i want them to run parallel. In Android i should be saving the job instance so i can cancel the job in the onDestroy method. Would it be acceptable to save each job separately in a list or am i breaking some kind of rule. I know in RX they have Subscriptions why isn't there an equivalent in Kotlin Coroutines?

val jobList = arrayListOf<Job>()

fun startJob1() {
    jobList.add(launch {
        //do some work
    })

fun startJob1() {
    jobList.add(launch {
        //do some other unrelated work
    })

override fun onDestroy() {
    super.onDestroy()
    cancelAllActiveJobs(jobList)
}

Does this type of architecture make sense for coroutines?

like image 854
BigApeWhat Avatar asked Jan 04 '23 05:01

BigApeWhat


2 Answers

You can manullay keep a list of Job objects that you launch, but you can also use a parent-child Job hierarhy that is available out-of-the box to manage and keep the list of launched jobs easier.

So, first, instead of a list of jobs, you define a reference to a parent job:

val job = Job()

Then, every time you lauch a new coroutine you make it a child of this job:

fun startJob1() {
    launch(job) { // make it a child
        //do some work
    }
}

fun startJob1() {
    launch(job) { // make it a child
        //do some other unrelated work
    }
}

Finally, when you need to destroy your object and cancel all the jobs you just cancel the parent job.

override fun onDestroy() {
    super.onDestroy()
    job.cancel()
}

The advantage of this apporoach is that the list of jobs is managed automatically. New coroutines can be launched and added to the parent job and when they complete they automatically remove themselves from the parent job.

You can read more in the corresponding section of the guide: https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md#cancellation-via-explicit-job

like image 121
Roman Elizarov Avatar answered Jan 05 '23 19:01

Roman Elizarov


That’s totally doable, and also nothing special. Look at this simple example which creates 100k jobs at once:

val jobs = List(100_000) { // launch a lot of coroutines and list their jobs
        launch {
            delay(1000L)
            print(".")
        }
    }
 jobs.forEach { it.join() } 

For making the jobs cancellable, it must itself check if it has been cancelled from outside, which you can do with a loop over the activity state: while (isActive).

Here's an example with two jobs that are cancelled afterwards:

fun main(args: Array<String>) = runBlocking {
    val startTime = System.currentTimeMillis()
    val jobs = arrayListOf<Job>()
    jobs += launch {
        var nextPrintTime = startTime
        var i = 0
        while (isActive) { // check if still active
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("Job1: Sleeping ${i++} ...")
                nextPrintTime += 500L
            }
        }
    }

    //another job
    jobs += launch {
        while (isActive) { // check if still active
            if (System.currentTimeMillis() >= 42) {
                println("Job2: Sleeping 42 ...")
                delay(500L)
            }
        }
    }
    delay(1300L) // delay a bit
    println("main: Cancelling the sleeping job!")
    jobs.forEach { it.cancelAndJoin() } // cancels the job and waits for its completion
}
like image 22
s1m0nw1 Avatar answered Jan 05 '23 20:01

s1m0nw1