'How to run two jobs in parallel but wait for another job to finish using kotlin coroutines

I have these jobs below:

  1. Inflating a complex calendar view async
  2. Downloading events A
  3. Downloading events B
  4. Adding calendar view to it's parent
  5. Adding A events to the calendar
  6. Adding B events to the calendar

I want

  • 1, 2, 3 have to start async,
  • 4 has to wait for 1
  • 5 has to wait for 2 and 4
  • 6 has to wait for 3 and 4
  • 6 and 5 should not be dependent to each other, can run at different times.
  • 4 is only dependent to 1, so it can run before 2 or 3 is finished.

I've tried async await but it makes them finish at the same time (as expected). I think this example might be a good way to learn parallel programming concepts, like semaphores mutex or spin locks. But it's too complicated for me to understand.

How should I implement these with using Kotlin coroutines?



Solution 1:[1]

It's pretty straightforward. All you need to do is:

  1. Implement CoroutineScope and create CoroutineContext, or use GlobalScope.
  2. Using local CoroutineScope or GlobalScope.launch() launch the coroutine.
  3. In the coroutine use async/await to run/wait asynchronous operations.

You can apply next code to your algorithm (all explanations are in the comments):

class SomeClass : CoroutineScope {
    private var job: Job = Job()

    // creating local CoroutineContext
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job

    // cancel the Job if it is no longer needed
    fun onClear() {
        job.cancel()
    }

    fun doJob() {
        // launch coroutine
        launch {
            // run Job1, Job2, Job3 in parallel, asyncIO - is an extension function on CoroutineScope
            val d1 = asyncIO { job1() }
            val d2 = asyncIO { job2() }
            val d3 = asyncIO { job3() }

            // waiting for result of Job1
            val job1Result = d1.await()

            // run Job4
            val d4 = asyncIO { job4(job1Result) }

            // waiting for result of Job2 and Job4
            val job2Result = d2.await()
            val job4Result = d4.await()

            // run Job5
            val d5 = asyncIO { job5(job2Result, job4Result) }

            // waiting for result of Job3
            val job3Result = d3.await()

            // run Job6
            val d6 = asyncIO { job6(job3Result, job4Result) }

            onDone(d5.await(), d6.await())
        }
    }

    private fun onDone(job5Result: String, job6Result: String) {
        // do something with result of Job5 and Job6
    }


    fun job1(): String {
        return "Result of job1"
    }

    fun job2(): String {
        return "Result of job2"
    }

    fun job3(): String {
        return "Result of job3"
    }

    fun job4(job1Result: String): String {
        return "Result of job4"
    }

    fun job5(job2Result: String, job4Result: String): String {
        return "Result of job5"
    }

    fun job6(job3Result: String, job4Result: String): String {
        return "Result of job6"
    }

    // extension function
    fun <T> CoroutineScope.asyncIO(ioFun: () -> T) = async(Dispatchers.IO) { ioFun() }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1