#1 Coroutine builder
kotlinx-coroutines-core
Core primitives to work with coroutines. Coroutine builder functions: Coroutine dispatchers implementing CoroutineDispatcher: More context elements: Synchronization primitives for coroutines: Top-level suspending functions: NameDescriptiondelayNon-blocking
kotlinlang.org
runBlocking์ ์ ์ธํ ์ฝ๋ฃจํด ๋น๋๋ CoroutinesScope์ ํ์ฅ ํจ์(Extension functions)๋ค. CoroutineScope๊ฐ ์ฝ๋ฃจํด ์ฝ๋ ์์ญ(Scope)๊ณผ ์๋ ์์ญ์ ๊ตฌ๋ถํ๋ ์ญํ ์ด๋ผ๋ฉด, Coroutine builder๋ ๊ทธ CoroutineScope ์์ญ์ ์ฝ๋ฃจํด ์ฝ๋๋ฅผ ์ํํ๋ ์ญํ ์ด๋ค. Coroutine builder๋ ์๋ก์ด ์ค๋ ๋๋ฅผ ์ค๋ ๋ ํ๋ก๋ถํฐ ํ ๋น๋ฐ๋ ํจ์์ด๊ธฐ๋ ํ๋ค. Coroutine builder์ ์ข
๋ฅ๋ ์๋์ 4๊ฐ์ง๋ค.
#2 kotlinx.coroutines.runBlocking
[Kotlin] Coroutines - runBlocking
#1 ์ด์ ๊ธ [Kotlin] Coroutines - Coroutine builder #1 Coroutine builder kotlinx-coroutines-core Core primitives to work with coroutines. Coroutine builder functions: Coroutine dispatchers implementing CoroutineDispatcher: More context elements: Synchron
kenel.tistory.com
runBlocking์ ์ ๊ฒ์๊ธ์์ ๋ค๋ฃฌ๋ค.
#3 Coroutine builder - CoroutineScope.launch
#3-1 ์ฝ๋
import kotlinx.coroutines.*
fun performTaskInCoroutineScope(): Job {
return CoroutineScope(Dispatchers.Default).launch {
var x = 1
repeat(5) {
println(x++)
delay(1000) // 1์ด ๋๊ธฐ
}
}
}
fun main() {
println("Start")
val myJob = performTaskInCoroutineScope()
println("Continuing...")
runBlocking {
// ํด๋น Job์ ์๋ฃ๋ฅผ ๊ธฐ๋ค๋ฆผ
myJob.join()
}
println("End")
}
/* ์ถ๋ ฅ ๊ฒฐ๊ณผ
Start
Continuing...
1
2
3
4
5
End
*/
CoroutineScope.launch๋ ํ์ฌ ์ค๋ ๋๋ฅผ Blocking(๋ธ๋กํน)ํ์ง ์๊ณ , CoroutineContext์ ๋ช
์๋ ์ค๋ ๋์์ ์ฝ๋ฃจํด์ ์คํํ๋ค. 'ํ์ฌ ์ค๋ ๋๋ฅผ ๋ธ๋กํนํ๋ค'๋ ๊ฒ์ 'ํด๋น ์ค๋ ๋์ ์ฝ๋ ๋์์ ์ผ์ ์ ์งํจ'์ ์๋ฏธํ๋ค. ๋ธ๋กํน์ ํ์ฌ ์ค๋ ๋์ ์ฝ๋ฃจํด์ฉ ์ค๋ ๋์ ๋ณ๋ ฌ์ ๋์ ์ฌ์ฉ์ด๋ผ๋, ์ฝ๋ฃจํด์ ์ผ๋ฐ์ ์ธ ๋ชฉ์ ์ ๋ถํฉํ์ง ์๋๋ค. ๋ฐ๋ผ์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ด๋ฉฐ ๋ํ์ ์ธ ์ฝ๋ฃจํด ํจ์์ธ launch๋ ํ์ฌ ์ค๋ ๋๋ฅผ ๋ธ๋กํนํ์ง ์์์ผ ํ๋ ๊ฒ์ด๋ค.
#3-2 Job ์ธํฐํ์ด์ค
CoroutineScope.launch๋ ์ฝ๋ฃจํด ์ฝ๋์ ๋ ํผ๋ฐ์ค ์ญํ ์ ํ๋ Job ์ธํฐํ์ด์ค์ ๊ฐ์ฒด๋ฅผ returnํ๋ค. ์ด ๋ ํผ๋ฐ์ค ๊ฐ์ฒด๋ฅผ ๋ณ์์ ํ ๋นํด ํด๋น ์ฝ๋ฃจํด์ ์ฐธ์กฐ(์ด ๋งํฌ์ #6-2)ํ ์๋ ์๋ค. CoroutineScope.launch๋ Job()๋ง์ returnํ๋ค. ๋ฐ๋ผ์ ์ด ํจ์์์๋, ์ฝ๋ฃจํด ์ฝ๋ ์ ์ด๋ค ๊ณ์ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ํ ์ ์๋ค.
#3-3 Job.join()
join์ ์ฌ์ ์ ์๋ฏธ๋ '์ฐ๊ฒฐํ๋ค, ํฉ์ณ์ง๋ค'๋ค. join()์ ํตํด, ์ธ์ ์๋ฃ๋ ์ง ๋ชจ๋ฅด๋ ์ด๋ค ์ฝ๋ฃจํด์ ์๋ฃ๋ ์ํ๋ก ํ์ฌ์ ์ฐ๊ฒฐ์ง๋๋ค. ๋ง์ด ์ด๋ ค์ด๋ฐ ๊ทธ๋ฅ Job์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ํจ์๋ค. ์ ์ฝ๋์์ join()์ด runBlocking { ... } ์์ ๋ค์ด๊ฐ ์๋ ์ด์ ๋ ์ฌ๊ธฐ์ ์๋ค.
#4 Coroutine builder - CoroutineScope.async
#4-1 ์ฝ๋
import kotlinx.coroutines.*
fun calculateSumAsync(a: Int, b: Int): Deferred<Int> {
return CoroutineScope(Dispatchers.Default).async {
println("Calculating sum in background thread...")
delay(1000) // 1์ด ๋์ ๋๊ธฐ (์ค์ ๋ก๋ ์ด๋ค ๊ณ์ฐ์ ์ํํ๋ ๊ฒ์ผ๋ก ๋์ฒด๋ ์ ์์)
return@async a + b // ๊ณ์ฐ ๊ฒฐ๊ณผ ๋ฐํ
}
}
fun main() {
println("Start")
val deferredResult: Deferred<Int> = calculateSumAsync(5, 10) // Deferred ๊ฐ์ฒด ์์ฑ
println("Doing some other work...")
// ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ค๋ฆผ
// await() ํจ์๋ฅผ ํธ์ถํ๋ฉด, ๋น๋๊ธฐ ์์
์ด ์๋ฃ๋ ๋๊น์ง ๋๊ธฐํ ํ ์์
๊ฒฐ๊ณผ๋ฅผ ๋ฐํํด์ค
runBlocking {
val result = deferredResult.await()
println("Result: $result")
}
println("End")
}
/* ์ถ๋ ฅ ๊ฒฐ๊ณผ
Start
Doing some other work...
Calculating sum in background thread...
Result: 15
End
*/
CoroutineScope.launch์ ๋ง์ฐฌ๊ฐ์ง๋ก ํ์ฌ ์ค๋ ๋๋ฅผ Blockingํ์ง ์๊ณ , CoroutineContext์ ๋ช
์๋ ์ค๋ ๋์์ ์ฝ๋ฃจํด์ ์คํํ๋ค. ํ์ง๋ง, CoroutineScope.async์ CoroutineScope.launch์ ๋ฌ๋ฆฌ Job()์ด ์๋ Deferred<T> ์ธํฐํ์ด์ค์ ์ธ์คํด์ค๋ฅผ returnํ๋ค.
#4-2 Deferred<T> ์ธํฐํ์ด์ค
Deferred<T> ์ธํฐํ์ด์ค๋ Job ์ธํฐํ์ด์ค์ ์์์ด๋ค. ๋ฐ๋ผ์ ๋ชจ๋ Deferred๋ Job์ด์ง๋ง, ๋ชจ๋ Job์ด Deferred์ธ ๊ฒ์ ์๋๋ค. Deferred๋ Job์ ๋ค๋ค๋ ๊ฒ์ฒ๋ผ ๋์ผํ๊ฒ ์ฐธ์กฐํด ์ฌ์ฉํ ์ ์๋ค. ํ์ง๋ง Deferred๋ Job๊ณผ ๋ฌ๋ฆฌ, (๋ฐ์ดํฐ ํ์์ด T์ธ) ๊ฐ์ฒด๋ฅผ ์ ์ฅํ๋ค. ๊ทธ๋์ CoroutineScope.async์์๋ ์ฝ๋ฃจํด ์ฝ๋ ์ ์ด๋ค ๊ณ์ฐ์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์ค๊ฑฐ๋ ํ ์ ์๋ค.
Defer์ ์ฌ์ ์ ์๋ฏธ๋ '๋ฏธ๋ฃจ๋ค, ์ฐ๊ธฐํ๋ค'๋ค. Deferred๋ ์๋ํ๋๊น, '๋ฏธ๋ค์ง, ์ฐ๊ธฐ๋' ์ ๋์ ๋ป์ผํ
๋ค. ๋ง ๊ทธ๋๋ก Deferred<T> ์ธํฐํ์ด์ค๋ '๋ฏธ๋ค์ง, ์ฐ๊ธฐ๋' ๊ฐ์ ์ ์ฅํ๋ค. ๊ทธ๋ ๊ทธ๋ด๊ฒ, CoroutineScope.async๊ฐ ์ํ๋ ์ค๋ ๋์์ ๊ณ์ฐ์ด ์๋ฃ๋์ด์ผ๋ง ๋น๋ก์ Deferred<T> ๊ฐ์ฒด๊ฐ ์๋ ์ค๋ ๋๋ก ๊ฒฐ๊ด๊ฐ์ด ์ ๋ฌ๋ ๊ฒ์ด๊ธฐ์ ํ์ฐ์ ์ผ๋ก '๊ฐ์ด ๋ฏธ๋ค์ง ์๋ฐ์' ์๋ ๊ฒ์ด๋ค.
#4-3 Deferred.await()
'๋ฏธ๋ค์ง' ๊ฐ์ ๋ฐ๋ ๋ฐฉ์์ด๋ฏ๋ก Deferred๊ฐ ์ ์ฅํ ๊ฐ์ ๋ฐ๋ ๋ฉค๋ฒ ๋ฉ์๋ ์ด๋ฆ๋ '๊ธฐ๋ค๋ฆฌ๋ค'๋ผ๋ ๋ป์ await๋ค. ์ ์ฝ๋์์ await()๊ฐ runBlocking { ... } ์์ ๋ค์ด๊ฐ ์๋ ์ด์ ๋ ์ฌ๊ธฐ์ ์๋ค.
#5 Coroutine builder - CoroutineScope.produce
#5-1 ์ฝ๋
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun produceNumbers(): ReceiveChannel<Int> {
return CoroutineScope(Dispatchers.Default).produce {
var x = 1
while (true) {
send(x++) // ๊ฐ์ ์ฑ๋๋ก ๋ณด๋
delay(1000) // 1์ด ๋๊ธฐ
}
}
}
fun main() {
val numbers: ReceiveChannel<Int> = produceNumbers() // ReceiveChannel ์์ฑ
repeat(5) {
runBlocking { println(numbers.receive()) } // ์ฑ๋์์ ๊ฐ ์์
}
println("Done receiving")
// ReceiveChannel์ ๋ซ์
numbers.cancel()
}
/* ์ถ๋ ฅ ๊ฒฐ๊ณผ
1
2
3
4
5
Done receiving
*/
produce๋ launch ๋ฐ async์ ๋ง์ฐฌ๊ฐ์ง๋ก ํ์ฌ ์ค๋ ๋๋ฅผ Blockingํ์ง ์๊ณ , CoroutineContext์ ๋ช
์๋ ์ค๋ ๋์์ ์ฝ๋ฃจํด์ ์คํํ๋ค. produce๋ async๋ฅผ ์ฌํ์ํจ ํํ๋ค. async์ ํ ๋ฒ ๊ฐ์ returnํ๊ณ ๋๋๋ ๋ฐ ๋ฐํด, produce๋ ๊ณ์ํด์ return ๊ฐ์ ์์ฐ(produce)ํด๋ธ๋ค. ๋ฐ๋ผ์ ๋ณต์ ๊ฐ์ ๊ฐ์ ์ป์ ์ ์๋ค. CoroutineScope.produce๋ CoroutineScope.async๊ณผ ๋ฌ๋ฆฌ Deferred๊ฐ ์๋ ReceiveChannel<E> ์ธํฐํ์ด์ค์ ์ธ์คํด์ค๋ฅผ returnํ๋ค.
#5-2 ReceiveChannel<E> ์ธํฐํ์ด์ค
ReceiveChannal์ ๋ณต์ ๊ฐ์ ๊ฐ์ ์ ๊ณตํ๋ Deferred ๋น์ค๋ฌด๋ฆฌํ ๊ฒ์ด๋ผ๋ ์ ์์, Deferred์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ถ๋ชจ๊ฐ Job์ด์ง ์์๊น ์ถ์ธก๋์ง๋ง ์ค์ ๊ทธ๋ ์ง ์๋ค. ๊ทธ๋๋ ๋๋์ผ๋ก ๋น์ทํ๋ค. Job.cancel๊ณผ ๊ฑฐ์ ๊ฐ์ ๊ธฐ๋ฅ์ ํ๋ ReceiveChannel.cancel ๋ฉ์๋๋ ์ง๋๊ณ ์๋ค.
#5-3 ReceiveChannel.receive()
receive()๋ ๋ฐ์ ๋๋ง๋ค ๊ฐ์ด ๋ฌ๋ผ์ง๋ค. ์ ์ฝ๋์์ receive()๊ฐ runBlocking { ... } ์์ ๋ค์ด๊ฐ ์๋ ์ด์ ๋ ์ฌ๊ธฐ์ ์๋ค.
#6 CoroutineScope ์๋ต
#6-1 CoroutineScope ์ค์ฒฉ
import kotlinx.coroutines.*
suspend fun main() {
val myJob = CoroutineScope(Dispatchers.Default).launch {
println("I'm in launch() function on ${Thread.currentThread().name} thread")
println("Async will be started after 2 seconds")
delay(2000)
val asyncResult = CoroutineScope(Dispatchers.Default).async {
println("I'm in async() function on ${Thread.currentThread().name} thread")
println("Calculating...")
delay(3000)
return@async 777
}
println("asyncResult is ${asyncResult.await()}")
}
myJob.join()
}
/* ์ถ๋ ฅ ๊ฒฐ๊ณผ
I'm in launch() function on DefaultDispatcher-worker-1 thread
Async will be started after 2 seconds
I'm in async() function on DefaultDispatcher-worker-2 thread
Calculating...
asyncResult is 777
*/
์ ์ฝ๋์ฒ๋ผ ์ฝ๋ฃจํด์ ์ค์ฒฉํด์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค. ๊ทธ๋ฌ๋, ์ด์ ๊ฐ์ด CoroutineScope(CoroutineContext)๋ฅผ ๊ณ์ ์ฌ์ฉํ๋ ๊ฒ์ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ์๋นํ ํด์น๋ค. fun ์์ ์๋ suspend ํค์๋๋ ์ฌ๊ธฐ์์ ์ค๋ช ํ๋ค. ๋ฐ๋ผ์ ์ผ๋จ ๋ณธ ๊ฒ์๊ธ์์ ๋ฌด์ํ๊ณ , CoroutineScope์ ์ค์ฒฉ์๋ง ์ง์คํ์.
#6-2 CoroutineScope ์๋ต - CoroutineContext ์์์ ์์
import kotlinx.coroutines.*
suspend fun main() {
val myJob = CoroutineScope(Dispatchers.Default).launch {
println("I'm in launch() function on ${Thread.currentThread().name} thread")
println("Async will be started after 2 seconds")
delay(2000)
val asyncResult = async {
println("I'm in async() function on ${Thread.currentThread().name} thread")
println("Calculating...")
delay(3000)
return@async 777
}
println("asyncResult is ${asyncResult.await()}")
}
myJob.join()
}
/* ์ถ๋ ฅ ๊ฒฐ๊ณผ
I'm in launch() function on DefaultDispatcher-worker-1 thread
Async will be started after 2 seconds
I'm in async() function on DefaultDispatcher-worker-2 thread
Calculating...
asyncResult is 777
*/
#6-1๊ณผ ๋์ผํ ๋์์ ํ๋ ์ฝ๋๋ค. ์ฝํ๋ฆฐ์์๋ ์ด๋ค CoroutineScope ๋ด์์ Builder๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ CoroutineScope(CoroutineContext)๋ฅผ ์๋ตํ ์ ์๋ค. ์ฝ๋์์ async()์ CoroutineScope๋ launch()์ ๋์ผํ ๊ฒ์ ์์๋ฐ์ ์ฌ์ฉํ๋ค.
#6-3 CoroutineScope ์๋ต - CoroutineContext ๋ณ๊ฒฝํ๊ธฐ
suspend fun main() {
val myJob = CoroutineScope(Dispatchers.Default).launch {
println("I'm in launch() function on ${Thread.currentThread().name} thread")
println("Async will be started after 2 seconds")
delay(2000)
val asyncResult = async(Dispatchers.IO + CoroutineName("myAsync")) {
println("I'm in async() function on ${Thread.currentThread().name} thread")
println("Calculating...")
delay(3000)
return@async 777
}
println("asyncResult is ${asyncResult.await()}")
}
myJob.join()
}
/* ์ถ๋ ฅ ๊ฒฐ๊ณผ
I'm in launch() function on DefaultDispatcher-worker-1 thread
Async will be started after 2 seconds
I'm in async() function on DefaultDispatcher-worker-2 thread
Calculating...
asyncResult is 777
*/
CoroutineScope๋ ์๋ตํ๋ฉด์, CoroutineContext๋ง ๋ณ๊ฒฝํ ์๋ ์๋ค. ๋ฐ๋ก, Builder์ ๋งค๊ฐ๋ณ์๋ก CoroutineContext๋ฅผ ์ ๋ฌํ๋ฉด ๋๋ค. ๋ฌธ๋ฒ์ CoroutineScope์ CoroutineContext๋ฅผ ์ ๋ฌํ๋ ๊ฒ๊ณผ ๋์ผํ๋ค. Dispatchers.Default์ Dispatchers.IO์ ์ค๋ ๋๋ช ์ด ๊ฐ์ ์ด์ ๋ ์ด ๋งํฌ์ #3-2์ ์๋ค.
#6-4 ์๋ต์ ๊ฐ๋ ์ฑ๋ง์ ์ํ ๊ฒ์ด ์๋๋ค
[Kotlin] Coroutines - ํ Scope ๋ด์์์ ๊ณ์ธต ๊ด๊ณ
#1 ์ด์ ๊ธ [Kotlin] Coroutines - Coroutine builder#1 Coroutine builder kotlinx-coroutines-coreCore primitives to work with coroutines. Coroutine builder functions: Coroutine dispatchers implementing CoroutineDispatcher: More context elements: Synchron
kenel.tistory.com
์ฌ์ค, #6-1๊ณผ #6-2๋ ๊ฐ์ ์ฝ๋๊ฐ ์๋๋ค. ์ฆ, CoroutineScope์ ์๋ต์ ๊ฐ๋ ์ฑ ํฅ์ ์ธ์ ์๋ฏธ๋ฅผ ๋ด๊ณ ์๋ค๋ ๋ง์ด๋ค. ๊ทธ๊ฒ์ ๋ฐ๋ก, ํ๋์ CoroutineScope ๋ด์์์ ๊ณ์ธต(๋ถ๋ชจ-์์) ๊ด๊ณ์ ๊ตฌํ์ด๋ค. #6-2 ์ฝ๋์์ launch๋ ๋ถ๋ชจ, async์ ์์์ด ๋๋ค. ๋ ์์ธํ ๋ด์ฉ์ ์ ๊ฒ์๊ธ์์ ๋ค๋ฃฌ๋ค.
#7 ์์ฝ
์ฝ๋ฃจํด ๋น๋ launch, async, produce ๊ฐ๊ฐ์ ๋ฐํํ์ Unit, Deferred<T>, ReceiveChannel<E>๋ค.
'๊นจ์ ๊ฐ๋ ๐ > Kotlin' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Kotlin] Coroutines - suspend ํค์๋ (0) | 2024.02.14 |
---|---|
[Kotlin] Coroutines - runBlocking (0) | 2024.02.13 |
[Kotlin] Coroutines - CoroutineScope, CoroutineContext (1) | 2024.02.10 |
[Kotlin] Coroutines - Suspend vs Block (0) | 2024.02.09 |
[Kotlin] Coroutines - ๋๊ธฐ ์ฝ๋, ๋น๋๊ธฐ ์ฝ๋ (0) | 2024.02.08 |