-
Notifications
You must be signed in to change notification settings - Fork 22
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.
- The presentation layer should only run on the
UI thread
- Any other long-running task must be offloaded to a separate thread, Which is not in the presentation layer
- If you look at
Fragment/Activity/ViewModels
you should not encounter a block of code that is running on a separate thread and all the code blocks must run on theUI thread
. -
So where to use the concurrency: We shall use it by encapsulating it in a
use-case
class. - Use-Case is also called
interactors
.
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