본문 바로가기

StackOverflow

Structured vs Unstructured coroutine

 

stackoverflow.com/questions/59368838/difference-between-coroutinescope-and-coroutinescope-in-kotlin

 

Difference between CoroutineScope and coroutineScope in Kotlin

Can anyone give clarity between functions CoroutineScope() and coroutineScope()? When I tried to check in source, I found that both of them are functions of CoroutineScope.kt. Additionally,

stackoverflow.com

Difference between CoroutineScope and coroutineScope in Kotlin

Asked 1 year, 4 months ago

Active today

Viewed 7k times

 

Can anyone give clarity between functions CoroutineScope() and coroutineScope()?

When I tried to check in source, I found that both of them are functions of CoroutineScope.kt. Additionally, coroutineScope() is suspend function while other one is normal function

 

 

asked Dec 17 '19 at 6:42

Kushal

 


Kotlin 에서 CoroutineScope 와 coroutineScope의 차이가 무엇인가요?

 

CoroutineScope 와 coroutineScope 의 차이가 명확하게 무엇인지 알려주실 분 있나요?

소스코드를 보니 둘 다 CoroutinScope.kt 파일 안에 있는 함수였습니다.

추가로, coroutineScope()은 suspend 함수였는데 대문자로 시작하는 CoroutineScope는 일반함수였습니다.

 

asked Dec 17 '19 at 6:42

Kushal

 

 


Answer 1

 

CoroutineScope() is nothing but a factory of CoroutineScope objects, and a CoroutineScope object is nothing but a holder of a CoroutineContext. It has no active role in coroutines, but it's an important part of the infrastructure that makes it easy to do structured concurrency properly. This comes from the fact that all coroutine builders like launch or async are extension functions on CoroutineScope and inherit its context.

 

You will rarely, if ever, have the need to call CoroutineScope() because usually you either pick up an existing coroutine scope or have one created for you by other convenience functions (like MainScope on Android) or Kotlin internals.

 

coroutineScope(), on the other hand, is a function that executes the block you pass it inside a sub-coroutine. It is basically an alias for withContext(this.coroutineContext) and you should primarily use it when you want to launch one or more background coroutines while you continue some work in the foreground, and then join on the background coroutines when completing the block.

 

answered Dec 17 '19 at 9:23

Marko Topolnik

178k

 



CoroutineScope() 는 단순히 CoroutineScope 객체의 팩토리이고, CoroutineScope 객체는 단순히 CoroutineContext 를 포함하고 있는 (holder) 객체입니다. 이것은 coroutine 에서 별다른 적극적인 역할을 하지 않지만, coroutine 기반체계 (infrastructure) 의 구조화 된 동시성 (Structured concurrency) 를 쉽게 쓰기 위한 중요한 파트입니다. 이것은 모든 coroutine builder (launch 또는 async와 같은) 가 CoroutineScope 의 확장함수이고 그 context를 상속받는다는 사실에서 비롯됩니다.

 

당신은 거의, 아니면 전혀 CoroutineScope() 를 호출하지 않을 것인데, 그 이유는 보통은 이미 있는 coroutine scope 또는 편하게 쓸 수 있도록 만들어 둔 (Android 의 MainScope 처럼) 함수를 쓰거나, 코틀린 내부함수를 쓸 것이기 때문입니다.

 

반면에 coroutineScope() 는 당신이 전달한 코드를 sub-coroutine으로 실행할 것입니다. 이것은 기본적으로 withContext(this.coroutineContext) 와 동일한 의미이며, 아마 당신은 여러 개의 코루틴을 백그라운드에서 실행시킨 다음, 포그라운드에서는 다른 작업을 하고, 백그라운드에서 실행된 결과를 모아서 포그라운드에 보여줄 때 사용하는 경우가 많을 것입니다.

 

answered Dec 17 '19 at 9:23

Marko Topolnik

178k

 


 

 

Answer 2

 

Best difference between CoroutineScope (Capital C version) vs coroutineScope (Smaller c version), I could figure out and which was easily understandable was correlating them with Unstructured vs Structured concurrency

Let me share an example :

 

class MainActivity : AppCompatActivity() {
    private lateinit var btn: Button
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        btn = findViewById(R.id.start_btn)
        btn.setOnClickListener {
            CoroutineScope(Dispatchers.Main).launch {
                val result = downloadUserData()
                Toast.makeText(applicationContext, "Result : $result", Toast.LENGTH_LONG).show()
            }
        }
    }

    private suspend fun downloadUserData(): Int {
        var result = 0
        // Here, we use CoroutineScope (Capital C version) which will start a new scope and
        // launch coroutine in new scope Dispatchers.IO, Not In Parent Scope which is Dispatchers.Main
        // Thus, this function would directly return without waiting for loop completion and will return 0
        CoroutineScope(Dispatchers.IO).launch {
            for (i in 0 until 100) {
                kotlinx.coroutines.delay(10)
                result++
            }
        }
        return result
    }
}


Output : Result : 0

 

This is an example of Unstructured Concurrency where it is not guaranteed that child coroutine would complete before returning. Thus, caller/parent coroutine would get wrong value returned by child coroutine. Even, when child coroutine has returned already, child coroutine may be running (in Active state) in the background which may lead to Memory Leaks in certain cases.

 

Solution :

When we need to communicate between multiple coroutines, we need to make sure Structured Concurrency (Recommended)

This can be done by re-using parent/caller coroutine scope inside child/callee coroutine. This can be achieved by coroutineScope {} (Smaller c) version inside child/callee coroutine.

 

private suspend fun downloadUserData(): Int {
    var result = 0
    // By using coroutineScope (Smaller c version) below, we ensure that this coroutine would execute in the
    // parent/caller coroutine's scope, so it would make sure that the for loop would complete
    // before returning from this suspended function. This will return 20000 properly
    coroutineScope {
        for (i in 0 until 100) {
            kotlinx.coroutines.delay(10)
            result++
        }
    }
    return result
}

 

 

answered Dec 17 '19 at 14:46

Kushal