Skip to content

What is Structured concurrency

Devrath edited this page Jan 13, 2024 · 12 revisions

Basic meaning of concurrency

Structured concurrency is a way of executing the concurrent code so that proper management of resources and the flow of control is done.

In the context of co-routines

In co-routines, provides some rules for managing the concurrency and execution of concurrent tasks in a controlled and disciplined manner.

Challenges faced by other approaches to concurrency

In traditional concurrency models such as threads or callback-based systems, It is challenging to manage the lifecycle of concurrent tasks -> Structured concurrency addresses this by a properly structured and hierarchical approach to manage it.

Basic Idea

Create a scope and launch the co-routines within the scope so that when the scope is canceled, all the co-routines in it are canceled.

Code

class SimpleStructuredConcurrencyDemoVm @Inject constructor( ) : ViewModel() {

    // Create a root co-routine scope
    private val rootScope =  CoroutineScope(Dispatchers.Default)

    fun startNestedIndependentCoroutines() {
        // Launch a co-routine within the scope
        rootScope.launch {
            println("Start outer coroutine")

            // Create a nested co-routine scope
            val nestedScope =  CoroutineScope(Dispatchers.Default)

            // Launch a new co-routine within the nested scope
            nestedScope.launch {
                println("Start inner coroutine")
                delay(10000) // Observe we keep delay longer here than the outer delay
                println("End inner coroutine")
            }

            delay(5000) // Observe we have kept outer delay lesser than inner delay

            println("End outer coroutine")
        }
    }

    fun startNestedLinkedCoroutines() {
        // Launch a co-routine within the scope
        rootScope.launch {
            println("Start outer coroutine")

            // Launch a new co-routine within the nested scope
            launch {
                println("Start inner coroutine")
                delay(10000) // Observe we keep delay longer here than the outer delay
                println("End inner coroutine")
            }

            delay(5000) // Observe we have kept the outer delay lesser than inner delay

            println("End outer coroutine")
        }
    }

    fun startNestedChildrenCoroutines() {
        // Launch a co-routine within the scope
        rootScope.launch {
            println("Start outer coroutine")

            // Launch a new co-routine within the nested scope
            launch {
                println("Start inner coroutine-1")
                delay(10000) // Observe we keep delay longer here than the outer delay
                println("End inner coroutine-1")
            }.join()

            launch {
                println("Start inner coroutine-2")
                delay(10000) // Observe we keep delay longer here than the outer delay
                println("End inner coroutine-2")
            }.join()

            println("End outer coroutine")
        }
    }

    fun cancel() {
        println("User invokes cancel")
        rootScope.cancel(cause = CancellationException("Cancelled explicitly by user"))
    }


}

Observations

  • startNestedIndependentCoroutines()
    • We have started 2 coroutines here one was an outer-scoped co-routine and another was launched from an outer co-routine scope.
    • But here we pass a different scope to the inner coroutine.
    • We have placed a delay in the outer co-routine scope just for our demo.
    • When we cancel the outer scope here before the delay of outer scope is complete, Only the outer coroutine is canceled.
    • The inner co-routine continues to completion.
  • startNestedLinkedCoroutines()
    • Conditions are the same as startNestedIndependentCoroutines but the context of both outer and inner co-routine is the same.
    • if we cancel the outer scope, both outer and inner co-routines are canceled
  • startNestedChildrenCoroutines()
    • Here we used join, we can see the synchronous communication.
    • It indicates the outer scope is started and the next in the inner coroutine-1 is started and continuous to completion and only then the next inner co-routine is started and continuous to completion.
    • Finally the outer co-routine is completed.
Clone this wiki locally