BottleH Blog

Kotlin in Action - 14์žฅ ์ฝ”๋ฃจํ‹ด

    Tags

  • Kotlin
Kotlin in Action - 14์žฅ ์ฝ”๋ฃจํ‹ด thumbnail

๐Ÿ“– 14.1 ๋™์‹œ์„ฑ๊ณผ ๋ณ‘๋ ฌ์„ฑ

  • ๋™์‹œ์„ฑ
    • ์—ฌ๋Ÿฌ ์ž‘์—…์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ
    • CPU ์ฝ”์–ด ํ•˜๋‚˜์—์„œ ๋™์‹œ์„ฑ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ๋ณ‘๋ ฌ์„ฑ
    • ์ฝ”๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋ˆ ์„œ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ
    • ์—ฌ๋Ÿฌ CPU ์ฝ”์–ด์—์„œ ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ

๐Ÿ“– 14.2 ์ฝ”ํ‹€๋ฆฐ์˜ ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•: ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜์™€ ์ฝ”๋ฃจํ‹ด

  • ์ฝ”๋ฃจํ‹ด์€ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์‹คํ–‰๋˜๋Š” ๋„Œ๋ธ”๋กœํ‚น ๋™์‹œ์„ฑ ์ฝ”๋“œ๋ฅผ ์šฐ์•„ํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.
  • ์Šค๋ ˆ๋“œ์— ๋น„ํ•ด ๋งค์šฐ ๊ฐ€๋ณ๋‹ค.
  • ๋™์‹œ์„ฑ ์ž‘์—…๊ณผ ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ ์ œ๊ณต

๐Ÿ“– 14.3 ์Šค๋ ˆ๋“œ์™€ ์ฝ”๋ฃจํ‹ด ๋น„๊ต

  • JVM์—์„œ ๋ณ‘๋ ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ๊ณผ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•œ ๊ณ ์ „์ ์ธ ์ถ”์ƒํ™”๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”
fun main() { println("I'm on ${Thread.currentThread().name}") thread { println("And I'm on ${Thread.currentThread().name}") } }
  • ์Šค๋ ˆ๋“œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋” ๋ฐ˜์‘์„ฑ ์žˆ๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๊ณ , ๋ฉ€ํ‹ฐ์ฝ”์–ด CPU์˜ ์—ฌ๋Ÿฌ ์ฝ”์–ด์— ์ž‘์—…์„ ๋ถ„์‚ฐ์‹œ์ผœ ํ˜„๋Œ€์  ์‹œ์Šคํ…œ์„ ๋” ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.
  • ์Šค๋ ˆ๋“œ ๊ฐ„ ์ „ํ™˜์€ ์šด์˜์ฒด์ œ ์ปค๋„ ์ˆ˜์ค€์—์„œ ์‹คํ–‰๋˜๋Š” ์ž‘์—…
  • ์Šค๋ ˆ๋“œ๊ฐ€ ์–ด๋–ค ์ž‘์—…์ด ์™„๋ฃŒ๋˜๊ธธ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ์—๋Š” ๋ธ”๋ก๋œ๋‹ค.
  • ์Šค๋ ˆ๋“œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋…๋ฆฝ์ ์ธ ํ”„๋กœ์„ธ์Šค๋กœ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž‘์—…์„ ๊ด€๋ฆฌํ•˜๊ณ  ์กฐ์ •ํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.

์ฝ”ํ‹€๋ฆฐ์€ ์Šค๋ ˆ๋“œ์— ๋Œ€ํ•œ ๋Œ€์•ˆ์œผ๋กœ ์ฝ”๋ฃจํ‹ด์ด๋ผ๋Š” ์ถ”์ƒํ™”๋ฅผ ๋„์ž…

  • ์ฝ”๋ฃจํ‹ด์€ ์ดˆ๊ฒฝ๋Ÿ‰ ์ถ”์ƒํ™”๋‹ค.
  • ์ฝ”๋ฃจํ‹ด์€ ์‹œ์Šคํ…œ ์ž์›์„ ๋ธ”๋ก์‹œํ‚ค์ง€ ์•Š๊ณ  ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ค‘๋‹จ ์ง€์ ์—์„œ ์žฌ๊ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ฝ”๋ฃจํ‹ด์€ ๊ตฌ์กฐํ™”๋œ ๋™์‹œ์„ฑ์ด๋ผ๋Š” ๊ฐœ๋…์„ ํ†ตํ•ด ๋™์‹œ ์ž‘์—…์˜ ๊ตฌ์กฐ์™€ ๊ณ„์ธต์„ ํ™•๋ฆฝํ•˜๋ฉฐ, ์ทจ์†Œ ๋ฐ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜ ์ œ๊ณต
  • ์ฝ”๋ฃจํ‹ด์€ ํ•˜๋‚˜ ์ด์ƒ์˜ JVM ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰

๐Ÿ“– 14.4 ์ž ์‹œ ๋ฉˆ์ถœ ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜: ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜

  • ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋ฃจํ‹ด์ด ์Šค๋ ˆ๋“œ, ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ, ์ฝœ๋ฐฑ๊ณผ ๊ฐ™์€ ๋‹ค๋ฅธ ๋™์‹œ์„ฑ ์ ‘๊ทผ ๋ฐฉ์‹๊ณผ ๋‹ค๋ฅธ ํ•ต์‹ฌ ์†์„ฑ์œผ๋กœ๋Š” ์ƒ๋‹จ์ˆ˜์˜ ๊ฒฝ์šฐ ์ฝ”๋“œ ํ˜•ํƒœ๋ฅผ ํฌ๊ฒŒ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ์ ์ด ์žˆ๋‹ค.

๐Ÿ”– 14.4.1 ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋Š” ์ˆœ์ฐจ์ ์œผ๋กœ ๋ณด์ธ๋‹ค

fun login(credentials: Credentials): UserID fun loadUserData(userID: UserID): UserData fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { val userID = login(credentials) val userData = loadUserData(userID) showData(userData) }
  • ๋ธ”๋ก๋œ ์Šค๋ ˆ๋“œ๋Š” ์ž์›์„ ๋‚ญ๋น„
suspend fun login(credentials: Credentials): UserID suspend fun loadUserData(userID: UserID): UserData fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { val userID = login(credentials) val userData = loadUserData(userID) showData(userData) }
  • suspend ๋ณ€๊ฒฝ์ž๋Š” ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰์„ ์ž ์‹œ ๋ฉˆ์ถœ ์ˆ˜๋„ ์žˆ๋‹ค๋Š” ๋œป
    • ex) ๋„คํŠธ์›Œํฌ ์‘๋‹ต์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒฝ์šฐ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จ
  • ์ผ์‹œ ์ค‘๋‹จ์€ ๊ธฐ์ € ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋ก์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.
  • ๋ฌผ๋ก  login๊ณผ loadUserData์˜ ๊ตฌํ˜„๋„ ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋ฃจํ‹ด์„ ๊ณ ๋ คํ•ด ์ž‘์„ฑ๋ผ์•ผ ํ•œ๋‹ค.

๐Ÿ“– 14.5 ์ฝ”๋ฃจํ‹ด์„ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ๋ฒ•๊ณผ ๋น„๊ต

fun loginAsync(credentials: Credentials, callback: (UserID) -> Unit) {} fun loadUserDataAsync(userID: UserID, callback: (UserData) -> Unit) {} fun showData(data: UserData) {} fun showUserInfo(credentials: Credentials) { loginAsync(credentials) { userID -> loadUserDataAsync(userID) { userData -> showData(userData) } } }
  • ๋กœ์ง์ด ์ปค์ง€๋ฉด ์ฝœ๋ฐฑ์ด ์ค‘์ฒฉ๋œ ์ฝ”๋“œ๊ฐ€ ๋ผ ๊ฐ€๋…์„ฑ์ด ๊ธ‰๊ฒฉํžˆ ๋–จ์–ด์ง„๋‹ค.
    • ์ฝœ๋ฐฑ ์ง€์˜ฅ
fun loginAsync(credentials: Credentials): CompletableFuture<UserID> fun loadUserDataAsync(userID: UserID): CompletableFuture<UserData> fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { loginAsync(credentials) .thenCompose { loadUserDataAsync(it) } .thenAccept { showData(it) } }
  • CompletableFuture์„ ์‚ฌ์šฉํ•˜๋ฉด ์ฝœ๋ฐฑ ์ง€์˜ฅ์„ ํ”ผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ์ƒˆ๋กœ์šด ์—ฐ์‚ฐ์ž์˜ ์˜๋ฏธ๋ฅผ ๋ฐฐ์›Œ์•ผ ํ•œ๋‹ค.
  • ํ•จ์ˆ˜์˜ ๋ฐ˜ํ™˜ ํƒ€์ž…๋„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค.
fun login(credentials: Credentials): Single<UserID> fun loadUserData(userID: UserID): Single<UserData> fun showData(data: UserData) fun showUserInfo(credentials: Credentials) { login(credentials) .flatMap { loadUserData(it) } .doOnSuccess { showData(it) } .subscribe() }
  • ๋ฐ˜์‘ํ˜• ์ŠคํŠธ๋ฆผ์„ ํ†ตํ•œ ๊ตฌํ˜„์€ ์ฝœ๋ฐฑ ์ง€์˜ฅ์„ ํ”ผํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ํ•จ์ˆ˜ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ๋ณ€๊ฒฝ ๋ฐ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์œ„์™€ ๋น„๊ตํ•ด๋ณด๋ฉด ์ฝ”๋ฃจํ‹ด์„ ์‚ฌ์šฉํ•˜๋Š” ์ ‘๊ทผ ๋ฐฉ์‹์—์„œ๋Š” ํ•จ์ˆ˜์— suspend ๋ณ€๊ฒฝ์ž๋งŒ ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

๐Ÿ”– 14.5.1 ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜ ํ˜ธ์ถœ

  • ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋Š” ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์ผ๋ฐ˜ ์ฝ”๋“œ ์•„๋ฌด ๊ณณ์—์„œ๋‚˜ ํ˜ธ์ถœํ•  ์ˆ˜๋Š” ใ…‡๋ฒ—๋‹ค.
  • ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ๋ธ”๋ก ์•ˆ์—์„œ๋งŒ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ๋Ÿฐ ๋ธ”๋ก ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋‹ค๋ฅธ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜์ผ ์ˆ˜ ์žˆ๋‹ค.
suspend fun mySuspendingFunction() {} fun main() { mySuspendingFunction() // error }
  • ์ผ๋ฐ˜์ ์ธ ์ผ์‹œ ์ค‘๋‹จ ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ์ฝ”๋“œ์—์„œ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ
  • main ํ•จ์ˆ˜๋ฅผ suspend๋กœ ํ•˜๋ฉด ํ˜ธ์ถœ ๊ฐ€๋Šฅ

๐Ÿ“– 14.6 ์ฝ”๋ฃจํ‹ด ์„ธ๊ณ„๋กœ ๋“ค์–ด๊ฐ€๊ธฐ: ์ฝ”๋ฃจํ‹ด ๋นŒ๋”

  • ์ฝ”๋ฃจํ‹ด์€ ์ผ์‹œ ์ค‘๋‹จ ๊ฐ€๋Šฅํ•œ ๊ณ„์‚ฐ์˜ ์ธ์Šคํ„ด์Šค๋‹ค.
  • ๋‹ค๋ฅธ ์ฝ”๋ฃจํ‹ด๋“ค๊ณผ ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ๋ธ”๋ก
  • ์Šค๋ ˆ๋“œ์™€ ๋น„์Šทํ•˜์ง€๋งŒ ์ฝ”๋ฃจํ‹ด์€ ํ•จ์ˆ˜ ์‹คํ–‰์„ ์ผ์‹œ ์ค‘๋‹จํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.
    • runBlocking์€ ๋ธ”๋กœํ‚น ์ฝ”๋“œ์™€ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜์˜ ์„ธ๊ณ„๋ฅผ ์—ฐ๊ฒฐํ•  ๋•Œ ์“ฐ์ธ๋‹ค.
    • launch๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ์ƒˆ๋กœ์šด ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•  ๋•Œ ์“ฐ์ธ๋‹ค.
    • async๋Š” ๋น„๋™๊ธฐ์ ์œผ๋กœ ๊ฐ’์„ ๊ณ„์‚ฐํ•  ๋•Œ ์“ฐ์ธ๋‹ค.

๐Ÿ”– 14.6.1 ์ผ๋ฐ˜ ์ฝ”๋“œ์—์„œ ์ฝ”๋ฃจํ‹ด์˜ ์„ธ๊ณ„๋กœ: runBlocking

suspend fun doSomethingSlowly() { delay(500.milliseconds) println("I'm done") } fun main() = runBlocking { doSomethingSlowly() }
  • ์ƒˆ ์ฝ”๋ฃจํ‹ด์„ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•˜๋ฉฐ, ํ•ด๋‹น ์ฝ”๋ฃจํ‹ด์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋ก์‹œํ‚จ๋‹ค.
  • runBlocking์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋ฅผ ๋ธ”๋กœํ‚นํ•œ๋‹ค.
  • ๋‹ค๋งŒ, ์ถ”๊ฐ€์ ์ธ ์ž์‹ ์ฝ”๋ฃจํ‹ด์„ ์–ผ๋งˆ๋“ ์ง€ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๊ณ , ์ด๋“ค์€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋ฅผ ๋” ์ด์ƒ ๋ธ”๋ก์‹œํ‚ค์ง€ ์•Š๋Š”๋‹ค.

๐Ÿ”– 14.6.2 ๋ฐœ์‚ฌ ํ›„ ๋ง๊ฐ ์ฝ”๋ฃจํ‹ด ์ƒ์„ฑ: launch ํ•จ์ˆ˜

private var zeroTime = System.currentTimeMillis() fun log(message: Any?) = println("${System.currentTimeMillis() - zeroTime} " + "[${Thread.currentThread().name}] $message") fun main() = runBlocking { log("The first, parent, coroutine starts") launch { log("The second coroutine starts and is ready to be suspended") delay(100.milliseconds) log("The second coroutine is resumed") } launch { log("The third coroutine can run in the meantime") } log("The first coroutine has launched two more coroutines") }
  • ์ด ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.
    • -Dkotlinx.coroutines.debug JVM ์˜ต์…˜ ํฌํ•จ ์‹คํ–‰
38 [main @coroutine#1] The first, parent, coroutine starts 52 [main @coroutine#1] The first coroutine has launched two more coroutines 53 [main @coroutine#2] The second coroutine starts and is ready to be suspended 58 [main @coroutine#3] The third coroutine can run in the meantime 163 [main @coroutine#2] The second coroutine is resumed
  • ๋ณ‘๋ ฌ์„ฑ ์—†์ด ๊ต์ฐจ ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ๋‹ค. ์ฆ‰, ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด์ด ๊ฐ™์€ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰
  • launch๋ฅผ ์‚ฌ์šฉํ•ด ์ƒˆ๋กœ์šด ๊ธฐ๋ณธ ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์ž‡๋‹ค.

๐Ÿ”– 14.6.3 ๋Œ€๊ธฐ ๊ฐ€๋Šฅํ•œ ์—ฐ์‚ฐ: async ๋นŒ๋”

suspend fun slowlyAddNumbers(a: Int, b: Int): Int { log("Waiting a bit before calculating $a + $b") delay(100.milliseconds * a) return a + b } fun main() = runBlocking { log("Starting the async computation") val myFirstDeferred = async { slowlyAddNumbers(2, 2) } val mySecondDeferred = async { slowlyAddNumbers(4, 4) } log("Waiting for the deferred value to be available") log("The first result: ${myFirstDeferred.await()}") log("The second result: ${mySecondDeferred.await()}") }
  • ์ด ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.
44 [main @coroutine#1] Starting the async computation 60 [main @coroutine#1] Waiting for the deferred value to be available 65 [main @coroutine#2] Waiting a bit before calculating 2 + 2 71 [main @coroutine#3] Waiting a bit before calculating 4 + 4 287 [main @coroutine#1] The first result: 4 481 [main @coroutine#1] The second result: 8
  • async๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•จ์œผ๋กœ์จ ๋‘ ๊ณ„์‚ฐ์ด ๋™์‹œ์— ์ผ์–ด๋‚˜๊ฒŒ ํ–ˆ๋‹ค.
  • await๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ๊ทธ Deferred์—์„œ ๊ฒฐ๊ด๊ฐ’์ด ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด์งˆ ๋•Œ๊นŒ์ง€ ๋ฃจํŠธ ์ฝ”๋ฃจํ‹ด์ด ์ผ์‹œ ์ค‘๋‹จ๋œ๋‹ค.
  • Deferred ๊ฐ์ฒด๋Š” ์•„์ง ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ๊ฐ’์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
    • ๋งค๋ž˜์— ์–ธ์  ๊ฐ€๋Š” ๊ฐ’์„ ์•Œ๊ฒŒ ๋  ๊ฒƒ์ด๋ผ๋Š” ์•ฝ์†, ์—ฐ๊ธฐ๋œ ๊ณ„์‚ฐ ๊ฒฐ๊ด๊ฐ’์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
๋นŒ๋” ๋ฐ˜ํ™˜๊ฐ’ ์“ฐ์ž„์ƒˆ
runBlocking ๋žŒ๋‹ค๊ฐ€ ๊ณ„์‚ฐํ•œ ๊ฐ’ ๋ธ”๋กœํ‚น ์ฝ”๋“œ์™€ ๋„Œ๋ธ”๋กœํ‚น ์ฝ”๋“œ ์‚ฌ์ด๋ฅผ ์—ฐ๊ฒฐ
launch Job ๋ฐœ์‚ฌ ํ›„ ๋ง๊ฐ ์ฝ”๋ฃจํ‹ด ์‹œ์ž‘(๋ถ€์ˆ˜ ํšจ๊ณผ๊ฐ€ ์žˆ์Œ)
async Deferred<T> ๊ฐ’์„ ๋น„๋™๊ธฐ๋กœ ๊ณ„์‚ฐ(๊ฐ’์„ ๊ธฐ๋‹ค๋ฆด ์ˆ˜ ์žˆ์Œ)

๐Ÿ“– 14.7 ์–ด๋””์„œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ• ์ง€ ์ •ํ•˜๊ธฐ: ๋””์ŠคํŒจ์ฒ˜

  • ์ฝ”๋ฃจํ‹ด์˜ ๋””์ŠคํŒจ์ฒ˜๋Š” ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•  ์Šค๋ ˆ๋“œ๋ฅผ ๊ฒฐ์ •
  • ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์„ ํƒํ•จ์œผ๋กœ์จ ์ฝ”๋ฃจํ‹ด์„ ํŠน์ • ์Šค๋ ˆ๋“œ๋กœ ์ œํ•œํ•˜๊ฑฐ๋‚˜ ์Šค๋ ˆ๋“œ ํ’€์— ๋ถ„์‚ฐ์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ฝ”๋ฃจํ‹ด์ด ํ•œ ์Šค๋ ˆ๋“œ์—์„œ๋งŒ ์‹คํ–‰๋ ์ง€ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋ ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ”– 14.7.1 ๋””์ŠคํŒจ์ฒ˜ ์„ ํƒ

  • ์ฝ”๋ฃจํ‹ด์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ถ€๋ชจ ์ฝ”๋ฃจํ‹ด์—์„œ ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์ƒ์†๋ฐ›์œผ๋ฏ€๋กœ ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด์— ๋Œ€ํ•ด ๋ช…์‹œ์ ์œผ๋กœ ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์ง€์ •ํ•  ํ•„์š”๋Š” ์—†๋‹ค.

๐Ÿ› ๏ธ ๋‹ค์ค‘ ์Šค๋ ˆ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ”์šฉ ๋””์ŠคํŒจ์ฒ˜: Dispatchers.Default

  • ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋””์ŠคํŒจ์ฒ˜
  • CPU ์ฝ”์–ด ์ˆ˜๋งŒํผ์˜ ์Šค๋ ˆ๋“œ๋กœ ๊ตฌ์„ฑ๋œ ์Šค๋ ˆ๋“œ ํ’€์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•จ
  • ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ ์ฝ”๋ฃจํ‹ด์ด ๋ถ„์‚ฐ๋ผ ์‹คํ–‰

๐Ÿ› ๏ธ UI ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰: Dispatchers.Main

  • UI ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํŠน์ • ์ž‘์—…์„ UI ์Šค๋ ˆ๋“œ๋‚˜ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ํŠน์ • ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•ด์•ผ ๋  ๋•Œ๊ฐ€ ์žˆ๋‹ค.
  • Dispatchers.Main์˜ ์‹ค์ œ ๊ฐ’์€ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๋‹ค.

๐Ÿ› ๏ธ ๋ธ”๋กœํ‚น๋˜๋Š” IO ์ž‘์—… ์ฒ˜๋ฆฌ: Dispatchers.IO

  • ์„œ๋“œํŒŒํ‹ฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฝ”๋ฃจํ‹ด์„ ์—ผ๋‘์— ๋‘๊ณ  ์„ค๊ณ„๋œ API๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์—†๋Š” ๊ฒฝ์šฐ
  • Dispatchers.IO์—์„œ ์‹คํ–‰๋œ ์ฝ”๋ฃจํ‹ด์€ ์ž๋™์œผ๋กœ ํ™•์žฅ๋˜๋Š” ์Šค๋ ˆ๋“œ ํ’€์—์„œ ์‹คํ–‰
  • CPU ์ง‘์•ฝ์ ์ด์ง€ ์•Š์€ ์ž‘์—…(๋ธ”๋กœํ‚น API ์‘๋‹ต ๋Œ€๊ธฐ)์— ์ ํ•ฉ
๋””์ŠคํŒจ์ฒ˜ ์Šค๋ ˆ๋“œ ๊ฐœ์ˆ˜ ์“ฐ์ž„์ƒˆ
Dispatchers.Default CPU ์ฝ”์–ด ์ˆ˜ ์ผ๋ฐ˜์ ์ธ ์—ฐ์‚ฐ, CPU ์ง‘์•ฝ์ ์ธ ์ž‘์—…
Dispatchers.Main 1 UI ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋งฅ๋ฝ์—์„œ๋งŒ ์ž‘์—…
Dispatchers.IO 64 + CPU ์ฝ”์–ด ๊ฐœ์ˆ˜(๋‹จ, ์ตœ๋Œ€ 64๊ฐœ๋งŒ ๋ณ‘๋ ฌ ์‹คํ–‰) ๋ธ”๋กœํ‚น IO ์ž‘์—…, ๋„คํŠธ์›Œํฌ ์ž‘์—…, ํŒŒ์ผ ์ž‘์—…
Dispatchers.Unconfined ์•„๋ฌด ์Šค๋ ˆ๋“œ๋‚˜ ์ฆ‰์‹œ ์Šค์ผ€์ค„๋งํ•ด์•ผ ํ•˜๋Š” ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ
limitedParallelism(n) ์ปค์Šคํ…€(n) ์ปค์Šคํ…€ ์‹œ๋‚˜๋ฆฌ์˜ค

๐Ÿ”– 14.7.2 ์ฝ”๋ฃจํ‹ด ๋นŒ๋”์— ๋””์ŠคํŒจ์ฒ˜ ์ „๋‹ฌ

fun main() { runBlocking { log("Doing some work") launch(Dispatchers.Default) { log("Doing some background work") } } }
  • ์ฝ”๋ฃจํ‹ด ๋นŒ๋” ํ•จ์ˆ˜๋Š” ์ฝ”๋ฃจํ‹ด ๋””์ŠคํŒจ์ฒ˜๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.
  • ์œ„ ์ฝ”๋“œ์˜ ์‹คํ–‰ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
45 [main @coroutine#1] Doing some work 63 [DefaultDispatcher-worker-2 @coroutine#2] Doing some background work

๐Ÿ”– 14.7.3 withContext๋ฅผ ์‚ฌ์šฉํ•ด ์ฝ”๋ฃจํ‹ด ์•ˆ์—์„œ ๋””์ŠคํŒจ์ฒ˜ ๋ฐ”๊พธ๊ธฐ

launch(Dispatchers.Default) { val result = performBackgroundOperation() withContext(Dispatchers.Main) { updateUI(result) } }
  • ์ด๋ฏธ ์‹คํ–‰ ์ค‘์ธ ์ฝ”๋ฃจํ‹ด์—์„œ ๋””์ŠคํŒจ์ฒ˜๋ฅผ ๋ฐ”๊ฟ€ ๋•Œ๋Š” withContext ํ•จ์ˆ˜์— ๋‹ค๋ฅธ ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์ „๋‹ฌ

๐Ÿ”– 14.7.4 ์ฝ”๋ฃจํ‹ด๊ณผ ๋””์ŠคํŒจ์ฒ˜๋Š” ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋งˆ๋ฒ• ๊ฐ™์€ ํ•ด๊ฒฐ์ฑ…์ด ์•„๋‹ˆ๋‹ค

fun main() { runBlocking { launch(Dispatchers.Default) { var x = 0 repeat(10_000) { x++ } println(x) } } }
  • ์ฝ”๋ฃจํ‹ด์ด ์ข…๋ฃŒ๋œ ํ›„ x ๊ฐ’์€ 10000์œผ๋กœ ์ •ํ™•ํ•˜๋‹ค.
  • ํ•œ ์ฝ”๋ฃจํ‹ด์ด ์ž„์˜์˜ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋”๋ผ๋„ ๊ทธ ๋กœ์ง์ด ์—„๊ฒฉํ•˜๊ฒŒ ์ˆœ์ฐจ์ ์œผ๋กœ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ
fun main() { runBlocking { var x = 0 repeat(10_000) { launch(Dispatchers.Default) { x++ } } delay(1.seconds) println(x) } }
  • ์นด์šดํ„ฐ ๊ฐ’์ด ์˜ˆ์ƒ๋ณด๋‹ค ๋‚ฎ๋‹ค.
  • ์—ฌ๋Ÿฌ ์ฝ”๋ฃจํ‹ด์ด ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ
fun main() = runBlocking { val mutex = Mutex() var x = 0 repeat(10_000) { launch(Dispatchers.Default) { mutex.withLock { x++ } } } delay(1.seconds) println(x) }
  • Mutex ์ž ๊ธˆ์„ ํ†ตํ•ด ์ฝ”๋“œ ์ž„๊ณ„ ์˜์—ญ์ด ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ์ฝ”๋ฃจํ‹ด๋งŒ ์‹คํ–‰๋˜๊ฒŒ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ“– 14.8 ์ฝ”๋ฃจํ‹ด์€ ์ฝ”๋ฃจํ‹ด ์ฝ˜ํ…์ŠคํŠธ์— ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋‹ค

  • ๊ฐ ์ฝ”๋ฃจํ‹ด์€ ์ถ”๊ฐ€์ ์ธ ๋ฌธ๋งฅ ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š”๋ฐ, ์ด ๋ฌธ๋งฅ์€ CoroutineContext๋ผ๋Š” ํ˜•ํƒœ๋กœ ์ œ๊ณต
  • CoroutineContext๋Š” ์—ฌ๋Ÿฌ ์š”์†Œ๋กœ ์ด๋ค„์ง„ ์ง‘ํ•ฉ
  • ์ด ์š”์†Œ ์ค‘ ํ•˜๋‚˜๋Š” ์ฝ”๋ฃจํ‹ด์ด ์–ด๋–ค ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋ ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๋””์ŠคํŒจ์ฒ˜
  • CoroutineContext์—๋Š” ์ฝ”๋ฃจํ‹ด์˜ ์ƒ๋ช…์ฃผ๊ธฐ์™€ ์ทจ์†Œ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” Job ๊ฐ์ฒด ํฌํ•จ
  • CoroutineContext์—๋Š” CoroutineNmae, CoroutineExceptionHandler์™€ ๊ฐ™์€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋„ ํฌํ•จ
suspend fun introspect() { log(coroutineContext) } fun main() { runBlocking { introspect() } }
  • ์œ„ ๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
39 [main @coroutine#1] [CoroutineId(1), "coroutine#1":BlockingCoroutine{Active}@32709393, BlockingEventLoop@3d99d22e] fun main() { runBlocking(Dispatchers.IO + CoroutineName("Coolroutine")) { introspect() } }
  • ์ฝ”๋ฃจํ‹ด ๋นŒ๋”์— ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ์ž์‹ ์ฝ”๋ฃจํ‹ด์˜ ์ฝ˜ํ…์ŠคํŠธ์—์„œ ํ•ด๋‹น ์š”์†Œ๋ฅผ ๋ฎ์–ด์“ด๋‹ค.
43 [DefaultDispatcher-worker-1 @Coolroutine#1] [CoroutineName(Coolroutine), CoroutineId(1), "Coolroutine#1":BlockingCoroutine{Active}@1be729a6, Dispatchers.IO]
Written by@BottleH
Back-End Developer

GitHub