https://proandroiddev.com/how-to-make-sense-of-kotlin-coroutines-b666c7151b93
The most interesting thing is that a thread can stop executing a coroutine at some specific “suspension points”, and go do some other work. It can resume executing the coroutine later on, or another thread could even take over.
๊ฐ์ฅ ํฅ๋ฏธ๋ก์ด ์ ์ ํ๋์ ์ค๋ ๋๊ฐ ํน์ "์ค๋จ ์ง์ ๋ค"์์ ์ฝ๋ฃจํด์ ์คํ์ ์ค์์ํฌ ์ ์๊ณ , ๋ค๋ฅธ ์์ ์ ๊ณ์ํ๋ค๋ ๊ฒ์ด์ฃ . ์ค๋ ๋๋ ๋์ค์ ๊ทธ ์ฝํ๋ฆฐ์ ์คํ์ ์ฌ๊ฐํ ์ ์๊ฑฐ๋, ๋ ๋ค๋ฅธ ์ค๋ ๋๊ฐ ๊ทธ ์ฝํ๋ฆฐ์ ์คํ์ ๋๊ฒจ ๋ฐ์ ์ ์์ต๋๋ค.
So, to be more accurate, one coroutine is not exactly one “task” but rather a sequence of “sub-tasks” to execute in a specific, guaranteed order. Even if the code seems to be in one sequential block, each call to a suspending function delimits the start of a new “sub-task” within the coroutine.
๊ทธ๋์, ๋ ์ ํํ๊ฒ ํ์๋ฉด, ํ๋์ ์ฝ๋ฃจํด์ ์ค์ ๋ก ํ๋์ "์์ "์ด ์๋๋ผ, ๋ณด์ฅ๋ ํน์ ์์๋ก ์คํํ๋ "ํ์ ์์ ๋ค"์ ์ํ์ค(์์๋ค)์ ๋๋ค. ์ฝ๋๊ฐ ํ๋์ ์์ฐจ์ ์ธ ๋ธ๋ก ์์ ์๋ ๊ฒ์ฒ๋ผ ๋ณด์ฌ๋, ๊ฐ๊ฐ์ ์ผ์์ค๋จ ํจ์์ ํธ์ถ์ ๊ทธ ์ฝ๋ฃจํด ์์์ ์๋ก์ด "ํ์ ์์ "์ ์์์ด ์ ํด์ง ๊ฒ์ ๋๋ค.
Suspending functions may suspend the execution of the current coroutine without blocking the current thread.
์ผ์ ์ค๋จ ํจ์๋ ํ์ฌ์ ์ค๋ ๋๋ฅผ ๋ธ๋กํ์ง ์๊ณ ํ์ฌ์ ์ฝ๋ฃจํด์ ์คํ์ ์ค๋จํ ์ ์๋ค.
Suspending functions are only asynchronous if they are explicitly used as such.
์ผ์ ์ค๋จ ํจ์๋ ๋ช ์์ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ์๋ง ๋น๋๊ธฐ์์ด๋ค.
From inside a suspending function, we can reason sequentially about function calls
์ผ์ ์ค๋จ ํจ์ ์์์ ์ฐ๋ฆฌ๋ ํจ์ ํธ์ถ๋ฅผ ์์ฐจ์ ์ผ๋ก ์ถ๋ก ํ ์ ์๋ค.
This is what makes asynchronous stuff easy to reason about in Kotlin. Inside a suspending function, calls to other suspending functions behave like normal function calls: we need to wait for the execution of the called function before getting the return value and executing the rest of the code.
๊ทธ๋์ ์ฝํ๋ฆฐ์์ ๋น๋๊ธฐ์ ์ธ ์์ ์ ์ฝ๊ฒ ์ถ๋ก ํ ์ ์๋ค. ์ผ์ ์ค๋จ ํจ์ ์์์, ๋ค๋ฅธ ์ผ์์ค๋จ ํจ์๋ค์ ํธ์ถ๋ค์ ์ผ๋ฐ ํจ์๋ค์ ํธ์ถ์ฒ๋ผ ๋์ํ๋ค. ๋ฆฌํด ๊ฐ์ ๋ฐ๊ณ ์ฝ๋์ ๋๋จธ์ง๋ฅผ ์คํํ๊ธฐ ์ ์ ์ฐ๋ฆฌ๋ ํธ์ถ๋ ํจ์์ ์คํ์ ๊ธฐ๋ค๋ฆฌ๋ฉด ๋๋ค.
The simplest way to deal with a suspending function from a normal function is to just block the current thread and wait. The coroutine builder to block the current thread is called runBlocking :
์ผ๋ฐ ํจ์์์ ์ผ์ ์ค๋จ ํจ์๋ฅผ ์ฒ๋ฆฌํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ํ์ฌ ์ฐ๋ ๋๋ฅผ ๋ง๊ณ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด๋ค. ํ์ฌ ์ค๋ ๋๋ฅผ ๋ง๋ ์ฝ๋ฃจํด ๋น๋๋ฅผ runBlocking์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
As you can see from its signature, the function passed to runBlocking is a suspending function, even though runBlocking itself is not suspending (it is thread-blocking):
fun <T> runBlocking(
...,
block: suspend CoroutineScope.() -> T
): T {
...
}
์๋ช (???)์์ ๋ณผ ์ ์๋ฏ์ด, runBlocking์ผ๋ก ์ ๋ฌ๋๋ ํจ์๊ฐ ์ผ์์ค๋จ ํจ์์ด๊ณ , runBlocking ์์ฒด๋ ์ผ์์ค๋จํ์ง ์๋๋ค(์ค๋ ๋๋ฅผ ๋ง๋ ๊ฒ์ด๋ค.)
[์ฝ๋]
Coroutine scopes are the tool that allows us to create a hierarchy of coroutines. This is what the Kotlin team calls structured concurrency.
์ฝ๋ฃจํด ์ค์ฝํ๋ค์ ์ฝ๋ฃจํด๋ค์ ๊ณ์ธต๊ตฌ์กฐ๋ฅผ ์์ฑํ๋ ๋๊ตฌ์ ๋๋ค. ์ฝํ๋ฆฐ ํ์ ์ด๊ฒ์ ๊ตฌ์กฐํ๋ ๋์์ฑ์ด๋ผ๊ณ ๋ถ๋ฆ ๋๋ค.
In Kotlin, coroutines can be created in a hierarchy, which allows a parent coroutine to automatically manage the life cycle of its child coroutines for you. It can for instance wait for its children to complete, or cancel all its children if an exception occurs in one of them.
์ฝํ๋ฆฐ์์, ์ฝ๋ฃจํด๋ค์ ๊ณ์ธต๊ตฌ์กฐ ์์์ ์์ฑ๋ ์ ์๊ณ , ๊ทธ๋ฌํ๊ธฐ์ ๋ถ๋ชจ ์ฝ๋ฃจํด์ด ์๋ ์ฝ๋ฃจํด๋ค์ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์๋์ ์ผ๋ก ๊ด๋ฆฌํฉ๋๋ค. ๋ถ๋ชจ ์ฝ๋ฃจํด์ ์์๋ค์ด ์๋ฃ๋ ๋๊น์ง ์ ์ ๊ธฐ๋ค๋ฆด ์ ์๊ฑฐ๋, ์์๋ค ์ค ํ๋์ ์์ธ๊ฐ ๋ฐ์ํ๋ฉด ๋ชจ๋ ์์๋ค์ ์ทจ์ํ ์ ์์ต๋๋ค.