๊พธ์ค€ํ•จ์ด ์ง„๋ฆฌ๋‹ค!!

์–ด์ œ๋ณด๋‹ค ๋ฐœ์ „ํ•œ ์˜ค๋Š˜์ด ๋˜๊ณ ํ”ˆ ๐Ÿง‘๐Ÿปโ€๐Ÿ’ป ์˜ ๋ธ”๋กœ๊ทธ

Tutorial/tutorial

[Kotlin] ์ฝ”ํ‹€๋ฆฐ Coroutine(์ฝ”๋ฃจํ‹ด)์— ๋Œ€ํ•˜์—ฌ (1) - CoroutineScope์™€ CoroutineContext ๋ฐ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฐฉ๋ฒ•

๋ށ์š” 2022. 10. 10. 00:53

 

์•ˆ๋…•ํ•˜์„ธ์š”

์˜ค๋Š˜์€ ์ฝ”ํ‹€๋ฆฐ์˜ Coroutine์— ๋Œ€ํ•˜์—ฌ

ํฌ์ŠคํŒ…ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

 

Coroutine์ด๋ž€?

ํ”„๋กœ๊ทธ๋ž˜๋ฐ์—์„œ ํ•˜๋‚˜์˜ ์ž‘์—… ๋‹จ์œ„๋ฅผ Routine์ด๋ผ ํ•˜๋Š”๋ฐ

๊ฐ Routine์ด ํ˜‘์—…(Co)ํ•˜์—ฌ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •์„ ํ•ฉ์น˜๊ฒŒ ๋˜์–ด Co + Roitine์ด๋ผ ๋ช…๋ช…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Coroutine์€ Thread์—์„œ ์‹คํ–‰๋˜๋Š” ์ž‘์€ Thread๋ผ ํ˜ธ์นญํ•  ์ˆ˜ ์žˆ์œผ๋‚˜ Thread์™€ ๊ฐ™์ด ์ž์‹ ์„ ์‚ฌ์šฉํ•  ์Šคํƒ์„

๋”ฐ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๊ฐ์ฒด ๋‹จ์œ„๋กœ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด Thread๋Œ€์‹  Coroutine์„ ๊ตณ์ด ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ผ๊นŒ์š”?? ๐Ÿค”

 

Thread๋“ค์„ ์‚ฌ์šฉํ•˜์—ฌ Thread๊ฐ„ Context Switching์‹œ ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋งŽ์ด ๋ฐœ์ƒํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ,

Thread๊ฐ€ Task ์‹คํ–‰๋„์ค‘ Block์ด ๋ฐ˜๋ณต ๋œ๋‹ค๋ฉด Thread ์„ฑ๋Šฅ์„ ์ œ๋Œ€๋กœ ํ™œ์šฉํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ์—

Coroutine์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

 

โœ”๏ธContext Switching์ด๋ž€?

ํ˜„์žฌ ์ง„ํ–‰ํ•˜๊ณ  ์žˆ๋Š” Task(Process, Thread)์˜ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๊ณ 

๋‹ค์Œ ์ง„ํ–‰ํ•  Task์˜ ์ƒํƒœ ๊ฐ’์„ ์ฝ์–ด ์ ์šฉํ•˜๋Š” ๊ณผ์ •์„ ๋งํ•ฉ๋‹ˆ๋‹ค.

์ฐธ์กฐ : https://nesoy.github.io/articles/2018-11/Context-Switching

 

Context Switching์ด๋ž€?

 

nesoy.github.io

 

 

์ด์ œ๋Š” Coroutine์˜ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์— ๋Œ€ํ•˜์—ฌ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 ๋จผ์ € Coroutine์€ Kotlin ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— Gradle ํŒŒ์ผ์— dependency๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

build.gradle์— ์˜์กด์„ฑ ์ถ”๊ฐ€

์ € ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ์œ„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์˜€์ง€๋งŒ ์•„๋ž˜ ๋งํฌ์—์„œ ์ฝ”๋ฃจํ‹ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฒ€์ƒ‰ ์‹œ ๋‹ค๋ฅธ ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฝ”๋ฃจํ‹ด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ํŽธ๋ฆฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์œผ๋‹ˆ ๊ถ๊ตผํ•˜์‹  ๋ถ„๋“ค์€ ํ•œ๋ฒˆ์”ฉ ํ™•์ธํ•ด๋ณด์‹œ๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค :)

 

https://mvnrepository.com/

 

์šฐ์„  Coroutine์—๋Š” ํฌ๊ฒŒ 2๊ฐ€์ง€์˜ interface๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ

๋ฐ”๋กœ CoroutineContext , CoroutineScope์ž…๋‹ˆ๋‹ค.

CoroutineContext๋ž€?

์ฝ”๋ฃจํ‹ด ์ฒ˜๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•  ๊ฒƒ์ธ์ง€์— ๋Œ€ํ•œ ์š”์†Œ(Element)๋“ค์˜ ์ง‘ํ•ฉ์œผ๋กœ Dispatcher์™€ Job์ด ์š”์†Œ๋“ค๋กœ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

Dispatcher์€ Coroutine์„ ์ฒ˜๋ฆฌํ•  Thread๋ฅผ Setting ํ•˜๊ณ  ํ• ๋‹นํ•˜๋Š” ์—ญํ• ์„

Job์€ ์ƒ์„ฑ๋œ Coroutine์„ ๊ด€๋ฆฌํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

 

 

Job๊ฐ์ฒด์˜ ํ•จ์ˆ˜๋“ค

ํ•จ์ˆ˜๋ช… ์„ค๋ช…
start() ํ˜„ ์ฝ”๋ฃจํ‹ด์˜ ์ƒํƒœ๋ฅผ ์•Œ์•„๋‚ด์–ด ์‹œ์ž‘ or ์ค€๋น„/์ข…๋ฃŒ
join() ํ˜„ ์ฝ”๋ฃจํ‹ด์ด ๋๋‚˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆผ
cancel() ํ˜„ ์ฝ”๋ฃจํ‹ด์„ ์ข…๋ฃŒ
cancelAndJoin() ํ˜„ ์ฝ”๋ฃจํ‹ด์„ ์ข…๋ฃŒํ•˜๊ณ  ๋Œ€๊ธฐ
cancelChildren() ํ˜„ CoroutineScope๋‚ด์— ์ž‘์„ฑ๋œ ๋ชจ๋“  ์ž์‹ ์ฝ”๋ฃจํ‹ด๋“ค์„ ์ข…๋ฃŒ

 

Dispatcher์˜ ์ข…๋ฅ˜ ๋ฐ ์„ค๋ช…

์ด๋ฆ„ ์„ค๋ช…
Dispatcher.Main ์•ˆ๋“œ๋กœ์ด๋“œ์—์„œ๋งŒ ์กด์žฌํ•˜๋Š” Dispatcher๋กœ ์•ˆ๋“œ๋กœ์ด๋“œ Main Thread(UI Thread)์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” Dispatcher์ž…๋‹ˆ๋‹ค.

โœ”๏ธ ๋ฐ˜๋“œ์‹œ UI์™€ ์ƒํ˜ธ์ž‘์šฉ์„ ํ•  ์‹œ์—๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
Dispatcher.IO ์ž…/์ถœ๋ ฅ ์ž‘์—…์— ํŠนํ™”๋œ Dispatcher
Dispatcher.Default CPU ์ž‘์—…๋Ÿ‰์ด ๋งŽ์€ ์ž‘์—…์— ์‚ฌ์šฉ๋˜๋Š” Dispatcher (Sorting , ๋ Œ๋”๋ง ๋“ฑ)
Dispatcher.UnConfined ํŠน์ˆ˜ํ•œ ์ƒํ™ฉ์—์„œ ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•  ๋•Œ ์‚ฌ์šฉ (์‚ฌ์šฉ์ด ๊ถŒ์žฅ๋˜์ง€ ์•Š์Œ ) 

 

CoroutineScope๋ž€?

Coroutine Block(Area)์„ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ Coroutine์ด ์‹คํ–‰๋˜๋Š” ๋ฒ”์œ„๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

CoroutineScope์˜ ํ™•์žฅ ํ•จ์ˆ˜์ธ Coroutine Builder๋ฅผ ํ†ตํ•ด ๋งŽ์ด ์‚ฌ์šฉํ•˜๋ฉฐ

๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” Builder๋Š” async์™€ launch์ž…๋‹ˆ๋‹ค.

 ์•„๋ž˜ ์˜ˆ์ œ ์ฝ”๋“œ์™€ ํ•จ๊ป˜ ์„ค๋ช…๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

 

launch 

๊ฒฐ๊ณผ ๋ฐ˜ํ™˜์ด ํ•„์š” ์—†๋Š” ์ž‘์—…์„ ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ Job๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ด ๋ฐ›์•„ ํ•ด๋‹น ์ฝ”๋ฃจํ‹ด์„ ๊ด€๋ฆฌ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

/**
	MutableList์— ์ €์žฅ๋œ ๊ฐ’๋“ค์„ ๋ชจ๋‘ ๋”ํ•˜๋Š” ํ™•์žฅ ํ•จ์ˆ˜
**/

class ExtensionFunctions {
    companion object{
        fun MutableList<Int>.elementSum() : Int {
            var sum = 0
            forEach{
                sum += it
            }
            return sum
        }
    }
}
import com.lee.coroutine.functions.ExtensionFunctions.Companion.elementSum
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

suspend fun main() {
    buildLaunch()
}

suspend fun buildLaunch() {
    println("${Thread.currentThread().name}์‹œ์ž‘")
    with(CoroutineScope(Dispatchers.Default)){
        var sum1 = 0
        var sum2 = 0
        var increment = 1
        val jobObj1 = launch{
            sum1 = (1..100).toMutableList().elementSum()
            delay(1000L)
            println("${Thread.currentThread().name}๋‚ด์˜ ์ฝ”๋ฃจํ‹ด ${increment++}์ข…๋ฃŒ")
        }

        val jobObj2 = launch {
            sum2 = (1..100).toMutableList().elementSum()
            delay(1000L)
            println("${Thread.currentThread().name}๋‚ด์˜ ์ฝ”๋ฃจํ‹ด ${increment++}์ข…๋ฃŒ")
        }

        jobObj1.join()
        jobObj2.join()
        println("๋‘ ๋ฆฌ์ŠคํŠธ์˜ ์ดํ•ฉ์€ ${sum1 + sum2}์ž…๋‹ˆ๋‹ค.")
    }
}

suspend ํ•จ์ˆ˜์— ๋Œ€ํ•ด์„œ..

suspend ํ‚ค์›Œ๋“œ๋Š” ํ•ด๋‹น ํ‚ค์›Œ๋“œ๋ฅผ ์„ ์–ธํ•จ์œผ๋กœ์จ ์ฝ”๋ฃจํ‹ด ๊ฐ์ฒด์—์„œ ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ์ž ์‹œ ํ•จ์ˆ˜๋ฅผ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ๋ฉˆ์ถฐ ์žˆ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ์ž‘์—…์ด ์ง„ํ–‰๋˜๋ฉฐ ๋ฉˆ์ถฐ์žˆ๋˜ ํ•จ์ˆ˜์˜ ์ž‘์—…์ด ์ข…๋ฃŒ๋˜๋ฉด ๋‹ค์‹œ ํ•จ์ˆ˜๋กœ ๋Œ์•„์™€ ๋‹ค์Œ ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋ฃจํ‹ด์—์„œ ๋ฉˆ์ถค๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” ๋ฐ˜๋“œ์‹œ suspend๋กœ ์„ ์–ธํ•ด์ฃผ์–ด์•ผ ํ•˜๋ฉฐ

suspend ํ•จ์ˆ˜๋Š” suspend๋กœ ์„ ์–ธ๋œ ํ•จ์ˆ˜ ๋‚ด๋ถ€ ํ˜น์€ ์ฝ”๋ฃจํ‹ด ๋ธ”๋Ÿญ์—์„œ๋งŒ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

 

์œ„ ์ฝ”๋“œ๋Š” 1~100์˜ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” 2๊ฐœ์˜ ๋ฆฌ์ŠคํŠธ์˜ ํ•ฉ์„ ๋”ํ•˜๋„๋ก ํ•˜๋Š” ๊ฐ„๋‹จํ•œ ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค.

 

CoroutineScope๋ฅผ ํ†ตํ•ด ์ฝ”๋ฃจํ‹ด์„ ์‚ฌ์šฉํ•  ๋ธ”๋Ÿญ์„ ์ง€์ •ํ•˜๊ณ  launch๋ฅผ ํ†ตํ•ด ์ฝ”๋ฃจํ‹ด์„ ์ƒ์„ฑํ•˜๊ณ  ์žˆ์œผ๋ฉฐ

๊ฐ ์ฝ”๋ฃจํ‹ด๋“ค์€ Job๊ฐ์ฒด๋กœ ๊ด€๋ฆฌ๋˜์–ด join()์„ ํ†ตํ•ด ์ž‘์—…์ด ๋๋‚  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค.

 

์‹คํ–‰ ํ™”๋ฉด

 

async

๊ฒฐ๊ณผ ๋ฐ˜ํ™˜ ๊ฐ’์ด ์žˆ์„ ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ ๋ฐ˜ํ™˜ ๊ฐ’์€ Deferred<T>์ž…๋‹ˆ๋‹ค.

await()๋ฅผ ํ†ตํ•ด Deferred์— ๊ฐ์‹ธ์ ธ ์žˆ๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜๋ฐ›์•„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

import com.lee.coroutine.functions.ExtensionFunctions.Companion.elementSum
import kotlinx.coroutines.*

suspend fun main() {
    buildAsync()
}

suspend fun buildAsync() {
    println("${Thread.currentThread().name} ์‹œ์ž‘")
    with(CoroutineScope(Dispatchers.Default)){
        var increment = 1
        var value1 = 0
        var value2 = 0
        val resultOne : Deferred<Int> = async {
            println("${Thread.currentThread().name}๋‚ด์˜ ์ฝ”๋ฃจํ‹ด ${increment++}์ข…๋ฃŒ")
            (1..100).toMutableList().elementSum()
        }

        val resultTwo : Deferred<Int> = async {
            println("${Thread.currentThread().name}๋‚ด์˜ ์ฝ”๋ฃจํ‹ด ${increment++}์ข…๋ฃŒ")
            (1..100).toMutableList().elementSum()
        }
        value1 = resultOne.await()
        value2 = resultTwo.await()
        println("๋‘ ๋ฆฌ์ŠคํŠธ์˜ ์ด ํ•ฉ์€ ${value1 + value2}์ž…๋‹ˆ๋‹ค.")
    }

}

์œ„ ์ฝ”๋“œ์˜ ๊ฒฐ๊ณผ ๊ฐ’์€ launch ์˜ˆ์ œ์™€ ๊ฐ™์œผ๋ฉฐ

launch ๋Œ€์‹  async๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’์„ ๋ฆฌํ„ด ๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

 

์œ„ 2๊ฐ€์ง€ ํ•จ์ˆ˜๋“ค ์™ธ์—๋„ withContext(CoroutineScope)์™€ runBlocking()์ด ์กด์žฌํ•˜๋ฉฐ

 

withContext๋Š” async์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ Deferred <T> ํƒ€์ž…์ด ์•„๋‹Œ ๋‹จ์ˆœํžˆ T ํƒ€์ž…์œผ๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

suspend fun buildWithContext() {
	val value = withContext(Dispatchers.Default){ 
    // ๋งˆ์ง€๋ง‰์— ๋ฐ˜ํ™˜๊ฐ’์„ ์ง€์ •ํ•ด์ฃผ๋ฉด Deferred๋กœ ๋ฌถ์ด์ง€ ์•Š๊ณ  ๋ฐ”๋กœ ๋ฐ˜ํ™˜๋œ๋‹ค.
    	(1..100).toMutableList().elementSum()
    }
    println("๊ฐ’์€ $value")
}

runBlocking()์€ Thread์—์„œ ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ ์‹œ  ํ˜ธ์ถœ๋œ ๋ถ€๋ถ„์—์„œ Block ๋˜์–ด runBlocking() ํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ 

์ฝ”๋ฃจํ‹ด๋“ค์ด ๋ชจ๋‘ ์ข…๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ํ•จ์ˆ˜๋ฅผ ์ •์ง€์‹œ์ผœ ๋™๊ธฐํ™”๋œ ๊ฐ’์„ ์–ป๊ณ ์ž ํ•  ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

 

 

 

์ด์ƒ์œผ๋กœ ์ฝ”๋ฃจํ‹ด์˜ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์„ ๋งˆ์น˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๋‹ค์Œ ํฌ์ŠคํŒ…์œผ๋กœ๋Š” Coroutine์˜ Channel๊ณผ Flow์— ๋Œ€ํ•˜์—ฌ ๊ณต๋ถ€ํ•œ ๋‚ด์šฉ์„ ํฌ์ŠคํŒ…ํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ˜Š

๊ทธ๋Ÿผ ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ ๋ต™๊ฒ ์Šต๋‹ˆ๋‹ค.

 

์˜ค๋Š˜๋„ ์ฆ์ฝ”ํ•˜์„ธ์š” :)