-
Notifications
You must be signed in to change notification settings - Fork 23
What is Structured concurrency
Devrath edited this page Jan 13, 2024
·
12 revisions
- Structured concurrency is a way of executing the concurrent code so that proper management of resources and the flow of control is done.
- Ability to pause the code execution and wait for all the concurrent flows to complete which can be traced to a specific ancestor to complete
In co-routines, provides some rules for managing the concurrency and execution of concurrent tasks in a controlled and disciplined manner.
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.
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.
In the case of unit tests, We use the test dispatchers who enable us to achieve the concurrency synchronously by completing the execution of SUT
before asserting a test case.
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"))
}
}
-
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
- Conditions are the same as
-
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.
- Here we used