Thread ?

์Šค๋ ˆ๋“œ ์ƒ์„ฑ

์ฝ”ํ‹€๋ฆฐ์€ ์Šค๋ ˆ๋“œ ์ƒ์„ฑ ๊ณผ์ •์„ ๋‹จ์ˆœํ™”ํ•ด์„œ ์‰ฝ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์ง€๊ธˆ์€ ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜์ง€๋งŒ, ์ดํ›„ ๊ณผ์ •์—์„œ๋Š” CPU ๋ฐ”์šด๋“œ์™€ I/O ๋ฐ”์šด๋“œ ์ž‘์—…์„ ๋ชจ๋‘ ํšจ์œจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์Šค๋ ˆ๋“œ ํ’€๋„ ์ƒ์„ฑํ•  ๊ฒƒ์ด๋‹ค.

CoroutineDispatcher

์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์Šค๋ ˆ๋“œ์™€ ์Šค๋ ˆ๋“œ ํ’€์„ ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ ์ง์ ‘ ์—‘์„ธ์Šคํ•˜๊ฑฐ๋‚˜ ์ œ์–ดํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์„ ์•Œ์•„์•ผ ํ•œ๋‹ค.

์—ฌ๊ธฐ์—์„œ๋Š” ์Šค๋ ˆ๋“œ๋ฅผ ํ•˜๋‚˜๋งŒ ๊ฐ–๋Š” CoroutineDispatcher๋ฅผ ์ƒ์„ฑํ•  ๊ฒƒ์ด๋ฉฐ, ๊ฑฐ๊ธฐ์— ์ถ”๊ฐ€ํ•˜๋Š” ๋ชจ๋“  ์ฝ”๋ฃจํ‹ด์€ ๊ทธ ํŠน์ • ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋œ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ๋‹จ ํ•˜๋‚˜์˜ ์Šค๋ ˆ๋“œ๋งŒ ๊ฐ–๋Š” CoroutineDispatcher๋ฅผ ํ™•์žฅํ•œ ThreadPoolDispatcher๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val netDispatcher = newSingleThreadContext(name = "ServiceCall")

    val task = GlobalScope.launch(netDispatcher) {
        printCurrentThread()
    }

    task.join()
}

๋””์ŠคํŒจ์ฒ˜์— ์ฝ”๋ฃจํ‹ด ๋ถ™์ด๊ธฐ

๋””์ŠคํŒจ์ฒ˜๊ฐ€ ๋งŒ๋“ค์–ด์กŒ๊ณ  ์ด์ œ ์ด ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

async ์ฝ”๋ฃจํ‹ด ์‹œ์ž‘

๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ชฉ์ ์œผ๋กœ ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ–ˆ๋‹ค๋ฉด async()๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. async()๋Š” ๋””ํผ๋“œ ์ฝ”๋ฃจํ‹ด ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ทจ์†Œ ๋ถˆ๊ฐ€๋Šฅํ•œ ๋„Œ ๋ธ”๋กœํ‚น ํ“จ์ฒ˜(non-blocking cancellable future)๋ฅผ ์˜๋ฏธํ•˜์—ฌ, T๋Š” ๊ทธ ๊ฒฐ๊ณผ์˜ ์œ ํ˜•์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val task = GlobalScope.async {
        doSomething()
    }
		task.join()
		println("Completed")
}

fun doSomething() {
    throw UnsupportedOperationException("Can't do")
}

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

async() ๋ธ”๋ก ์•ˆ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ์˜ˆ์™ธ๋Š” ๊ทธ ๊ฒฐ๊ณผ์— ์ฒจ๋ถ€๋˜๋Š”๋ฐ, ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•ด์•ผ ์˜ˆ์™ธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์œ„ํ•ด์„œ isCancelled์™€ getCancellationException() ๋ฉ”์†Œ๋“œ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ์•ˆ์ „ํ•˜๊ฒŒ ์˜ˆ์™ธ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

if (task.isCancelled) {
    val exception = task.getCancellationException()
    println("Error with message: ${exception.cause}")
} else {
    println("Success")
}

์˜ˆ์™ธ๋ฅผ ์ „ํŒŒํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋””ํผ๋“œ์—์„œ await()์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val task = GlobalScope.async {
        doSomething()
    }

    // This code will have the exception be propagated
    task.await()
		println("Success")
}

๊ทธ๋Ÿฌ๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋น„์ •์ƒ์ ์œผ๋กœ ์ค‘๋‹จ๋œ๋‹ค.

await()๋ฅผ ํ˜ธ์ถœํ•ด์„œ ์ค‘๋‹จ๋˜๋Š”๋ฐ ์ด ๊ฒฝ์šฐ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๊ฐ์‹ธ์ง€ ์•Š๊ณ  ์ „ํŒŒํ•˜๋Š” ๊ฐ์‹ธ์ง€ ์•Š์€ ๋””ํผ๋“œ(unwrapping deferred)๋‹ค.

join()์œผ๋กœ ๋Œ€๊ธฐํ•œ ํ›„ ๊ฒ€์ฆํ•˜๊ณ  ์–ด๋–ค ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ๊ณผ await()๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์˜ ์ฃผ์š” ์ฐจ์ด๋Š” join()์€ ์˜ˆ์™ธ๋ฅผ ์ „ํŒŒํ•˜์ง€ ์•Š๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ˜๋ฉด, await()๋Š” ๋‹จ์ง€ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ ์˜ˆ์™ธ๊ฐ€ ์ „ํŒŒ๋œ๋‹ค๋Š” ์ ์ด๋‹ค.

launch ์ฝ”๋ฃจํ‹ด ์‹œ์ž‘

๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๋Š” ์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•˜๋ ค๋ฉด launch()๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. launch()๋Š” ์—ฐ์‚ฐ์ด ์‹คํŒจํ•œ ๊ฒฝ์šฐ์—๋งŒ ํ†ต๋ณด ๋ฐ›๊ธฐ๋ฅผ ์›ํ•˜๋Š” fire-and-forget ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์œ„ํ•ด ์„ค๊ณ„๋์œผ๋ฉฐ, ํ•„์š”ํ•  ๋•Œ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋„ ํ•จ๊ป˜ ์ œ๊ณต๋œ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val task = GlobalScope.launch {
        doSomething()
    }

    // This code will have the exception be propagated
    task.join()
		println("Success")
}

์˜ˆ์ƒํ•œ ๋Œ€๋กœ ์˜ˆ์™ธ๊ฐ€ ์Šคํƒ์— ์ถœ๋ ฅ๋˜์ง€๋งŒ ์‹คํ–‰์ด ์ค‘๋‹จ๋˜์ง€ ์•Š์•˜๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ main()์˜ ์‹คํ–‰์„ ์™„๋ฃŒํ–ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์ฝ”๋ฃจํ‹ด์„ ์‹œ์ž‘ํ•  ๋•Œ ํŠน์ • ๋””์ŠคํŒจ์ฒ˜ ์‚ฌ์šฉํ•˜๊ธฐ

์ง€๊ธˆ๊นŒ์ง€๋Š” ๊ธฐ๋ณธ ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์‚ฌ์šฉํ–ˆ์ง€๋งŒ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํŠน์ • ์ฝ”๋ฃจํ‹ด ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

fun main(args: Array<String>) = runBlocking {
		val dispatcher = newSingleThreadContext(name = "ServiceCall")
    val task = GlobalScope.launch(dispatcher) {
        doSomething()
    }

    // This code will have the exception be propagated
    task.join()
		println("Success")
}

์š”์•ฝ

  • ๋„คํŠธ์›Œํฌ ์š”์ฒญ์€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•œ๋‹ค. ์—…๋ฐ์ดํŠธ๋˜๋Š” ๋ทฐ๋ฅผ ์œ„ํ•œ ์ •๋ณด๋Š” UI ์Šค๋ ˆ๋“œ๋กœ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

  • CoroutineDispatcher๋Š” ์ฝ”๋ฃจํ‹ด์„ ํŠน์ • ์Šค๋ ˆ๋“œ ๋˜๋Š” ์Šค๋ ˆ๋“œ ๊ทธ๋ฃน์—์„œ ์‹คํ–‰ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ•˜๋‚˜ ์ด์ƒ์˜ ์ฝ”๋ฃจํ‹ด์„ launch ๋˜๋Š” async๋กœ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • launch๋Š” ํŒŒ์ด์–ด์•คํฌ๊ฐฏ(fire-and-forget_์™€ ๊ฐ™์€ ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ ์‚ฌ์šฉ๋ผ์•ผ ํ•˜๋Š”๋ฐ, ์ฝ”๋ฃจํ‹ด์ด ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ฐ˜ํ™˜ํ•  ๊ฒƒ์„ ์˜ˆ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๋ฅผ ๋งํ•œ๋‹ค.

  • ๋™์‹œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์ง€๋งŒ, ๋ช…ํ™•ํ•˜๊ณ  ์•ˆ์ „ํ•˜๋ฉฐ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์ฝ”ํ‹€๋ฆฐ์˜ ์œ ์—ฐ์„ฑ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

Last updated

Was this helpful?