suspend ?

์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜

์ด๋ฆ„์ด๋‚˜ ์•„์ด๋””๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ”„๋กœํŒŒ์ผ์„ ๊ฒ€์ƒ‰ํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„ค๊ณ„ํ•ด๋ณด์ž. ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ๊ฐ–๋„๋ก ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด Deferred๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ํ•˜์ž

interface ProfileServiceRepository {
		fun asyncFetchByName(name: String) : Deferred<Profile>
		fun asyncFetchbyId(id: Long) : Deferred<Profile>
}

๋ชจ์˜ ๊ตฌํ˜„์€ ๊ฐ„๋‹จํ•˜๋‹ค.

class ProfileServiceClient : ProfileServiceRepository {
		override fun asyncFetchByName(name: String) = GlobalScope.async {
				Profile(1, name, 28)
		}
		fun asyncFetchbyId(id: Long) : GlobalScope.async {
				Profile(id, "Susan", 28)
		}
}

์ด ๊ตฌํ˜„์„ ํ˜ธ์ถœํ•ด๋ณด์ž.

fun main(args: Array<String>) = runBlocking {
		val client : ProfileServiceRepository = ProfileServiceClient()
		val profile = client.asyncFetchById(12).await()

		println(profile)
}

๊ตฌํ˜„์—์„œ ๊ด€์ฐฐํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ์ด ์žˆ๋‹ค.

  • ํ•จ์ˆ˜ ์ด๋ฆ„์ด ์ดํ•ดํ•˜๊ธฐ ํŽธํžˆํ•˜๊ฒŒ ๋ผ ์žˆ๋‹ค. ํ•„์š”ํ•  ๋•Œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์„ ์™„๋ฃŒํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋„๋ก, ํ•จ์ˆ˜๊ฐ€ ๋น„๋™๊ธฐ(async)๋ผ๋Š” ์ ์„ ๋ช…์‹œํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

  • ํ˜ธ์ถœ์ž๋Š” ํ•ญ์ƒ ์š”์ฒญ์ด ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ์ผ์‹œ์ •์ง€ํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋ณดํ†ต ํ•จ์ˆ˜ ํ˜ธ์ถœ ์งํ›„์— await() ํ˜ธ์ถœ์ด ์žˆ๊ฒŒ ๋œ๋‹ค.

  • ๊ตฌํ˜„์€ Deferred์™€ ์—ฎ์ด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ๋‹ค๋ฅธ ์œ ํ˜•์˜ ํ“จ์ฒ˜(feture)๋กœ ProfileServiceRepository ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•์€ ์—†๋‹ค. ์ฝ”ํ‹€๋ฆฐ์ด ์•„๋‹Œ ๋™์‹œ์„ฑ ๊ธฐ๋ณธํ˜•์œผ๋กœ ๊ตฌํ˜„ํ•˜๋ฉด ์ž์นซ ์ง€์ €๋ถ„ํ•ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ

์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•ด๋ณด์ž.

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

interface ProfileServiceRepository {
		suspend fun fetchByName(name: String) : Profile
		suspend fun fetchById(id: Long) : Profile
}

๊ตฌํ˜„ ๋˜ํ•œ ์‰ฝ๊ฒŒ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋‹ค.

class ProfileServiceClient : ProfileServiceRepository {
		override suspend fun fetchByName(name: String) : Profile {
				return Profile(1, name, 28)
		}
		override suspend fun fetchById(name: String) : Profile {
				return Profile(id, "Susan", 28)
		}
}

์ด ๋ฐฉ์‹์€ ๋น„๋™๊ธฐ ๊ตฌํ˜„์— ๋น„ํ•ด ๋ช‡ ๊ฐ€์ง€ ๋ถ„๋ช…ํ•œ ์ด์ ์ด ์žˆ๋‹ค.

  • ์œ ์—ฐํ•จ : ์ธํ„ฐํŽ˜์ด์Šค์˜ ์ƒ์„ธ ๊ตฌํ˜„ ๋‚ด์šฉ์€ ๋…ธ์ถœ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ํ“จ์ฒ˜๋ฅผ ์ง€์›ํ•˜๋Š” ๋ชจ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ตฌํ˜„์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๊ฐ„๋‹จํ•จ : ์ˆœ์ฐจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋ ค๋Š” ์ž‘์—…์— ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•ญ์ƒ await()๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•˜๋Š” ๋ฒˆ๊ฑฐ๋กœ์›€์ด ์ƒ๊ธฐ๊ณ , ๋ช…์‹œ์ ์œผ๋กœ async๊ฐ€ ํฌํ•จ๋œ ํ•จ์ˆ˜์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜์™€ ๋น„๋™๊ธฐ ํ•จ์ˆ˜

๋น„๋™๊ธฐ ํ•จ์ˆ˜ ๋Œ€์‹  ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์ด๋“œ๋ผ์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ์ผ๋ฐ˜์ ์œผ๋กœ ๊ตฌํ˜„์— Job์ด ์—ฎ์ด๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•  ๋•Œ๋Š” ํ•ญ์ƒ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๋น„๋™๊ธฐ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Job์„ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ์œ„ํ•œ ๊ตฌํ˜„์„ ํ•ด์•ผ ํ•œ๋‹ค.

  • ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ถ”์ƒ(abstract) ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ๋•Œ๋Š” ํ•ญ์ƒ ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ฐ€์‹œ์„ฑ์ด ๋†’์€ ํ•จ์ˆ˜์ผ์ˆ˜๋ก ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์ฝ”๋ฃจํ‹ด ์ปจํ…์ŠคํŠธ

์ฝ”๋ฃจํ‹ด์€ ํ•ญ์ƒ ์ปจํ…์ŠคํŠธ ์•ˆ์—์„œ ์‹คํ–‰๋œ๋‹ค. ์ปจํ…์ŠคํŠธ๋Š” ์ฝ”๋ฃจํ‹ด์ด ์–ด๋–ป๊ฒŒ ์‹คํ–‰๋˜๊ณ  ๋™์ž‘ํ•ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์š”์†Œ๋“ค์˜ ๊ทธ๋ฃน์ด๋‹ค.

๋””์ŠคํŒจ์ฒ˜

๋””์ŠคํŒจ์ฒ˜(Dispatcher)๋Š” ์ฝ”๋ฃจํ‹ด์ด ์‹คํ–‰๋  ์Šค๋ ˆ๋“œ๋ฅผ ๊ฒฐ์ •ํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์—๋Š” ์‹œ์ž‘๋  ๊ณณ๊ณผ ์ค‘๋‹จ ํ›„ ์žฌ๊ฐœ๋  ๊ณณ์„ ๋ชจ๋‘ ํฌํ•จ๋œ๋‹ค.

CommonPool

CommonPool์€ CPU ๋ฐ”์šด๋“œ ์ž‘์—…์„ ์œ„ํ•ด์„œ ํ”„๋ ˆ์ž„์›Œํฌ์— ์˜ํ•ด ์ž๋™์œผ๋กœ ์ƒ์„ฑ๋˜๋Š” ์Šค๋ ˆ๋“œ ํ’€์ด๋‹ค. ์Šค๋ ˆ๋“œ ํ’€์˜ ์ตœ๋Œ€ ํฌ๊ธฐ๋Š” ์‹œ์Šคํ…œ์˜ ์ฝ”์–ด ์ˆ˜์—์„œ 1์„ ๋บ€ ๊ฐ’์ด๋‹ค. ํ˜„์žฌ๋Š” ๊ธฐ๋ณธ ๋””์ŠคํŒจ์ฒ˜๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

์˜ˆ์™ธ ์ฒ˜๋ฆฌ

์ฝ”๋ฃจํ‹ด ์ปจํ…์ŠคํŠธ์˜ ๋˜ ๋‹ค๋ฅธ ์ค‘์š”ํ•œ ์šฉ๋„๋Š” ์˜ˆ์ธก์ด ์–ด๋ ค์šด ์˜ˆ์™ธ์— ๋Œ€ํ•œ ๋™์ž‘์„ ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ์ปจํ…์ŠคํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด CoroutineExceptionHandler๋ฅผ ๊ตฌํ˜„ํ•ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

fun main(args: Array<String>) = runBlocking {
		val handler = CoroutineExceptionHandler({ context, throwable ->
				println("Error captured in $context")
				println("Message: ${throwable.message}")
		})

		GlobalScope.launch(handler) {
				TODO("Not implemented yet!")
		}
}

Non-cancellable

์•ž์—์„œ ๋‹ค๋ฃฌ ๊ฒƒ์ฒ˜๋Ÿผ ์ฝ”๋ฃจํ‹ด์˜ ์‹คํ–‰์ด ์ทจ์†Œ๋˜๋ฉด ์ฝ”๋ฃจํ‹ด ๋‚ด๋ถ€์— CancellationException ์œ ํ˜•์˜ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์ฝ”๋ฃจํ‹ด์ด ์ข…๋ฃŒ๋œ๋‹ค.

suspend fun nonCancellable() {
    val duration = measureTimeMillis {
        val job = GlobalScope.launch {
            try {
                while (isActive) {
                    delay(500)
                    println("still running")
                }
            } finally {
                withContext(NonCancellable) { // NoneCancellable ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฝ”๋ฃจํ‹ด์ด ์ทจ์†Œ ๋˜๋”๋ผ๋„ ์ค‘์ง€ ๋˜์ง€ ์•Š๋Š”๋‹ค. 
                    println("cancelled, will delay finalization now")
                    delay(5000)
                    println("delay completed, bye bye")
                }
            }
        }

        delay(1200)
        job.cancelAndJoin()
    }

    println("Took $duration ms")
}

NonCancellable ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์ทจ์†Œ ์‹œ delay๊ฐ€ ์ผ์–ด๋‚˜์ง€ ์•Š๊ณ  ์ข…๋ฃŒ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ฝ”๋ฃจํ‹ด์ด ์ทจ์†Œ๋˜๋Š” ๋™์•ˆ ์ผ์‹œ ์ค‘์ง€๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ NonCancellable ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

์ปจํ…์ŠคํŠธ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด

์ปจํ…์ŠคํŠธ ๊ฒฐํ•ฉ

์ปจํ…์ŠคํŠธ์˜ ์ผ๋ถ€๋ถ„์ด ๋  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ ์š”์†Œ๊ฐ€ ์žˆ๋‹ค. ๋‹ค์–‘ํ•œ ์š”๊ตฌ์‚ฌํ•ญ์„ ๋งŒ์กฑํ•˜๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ด๋Ÿฌํ•œ ์š”์†Œ๋“ค์„ ๊ฒฐํ•ฉ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

์ปจํ…์ŠคํŠธ ์กฐํ•ฉ

ํŠน์ • ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰ํ•˜๋Š” ์ฝ”๋ฃจํ‹ด์„ ์‹คํ–‰ํ•˜๊ณ  ๋™์‹œ์— ํ•ด๋‹น ์Šค๋ ˆ๋“œ๋ฅผ ์œ„ํ•œ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๋ฅผ ์„ค์ •ํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์ž.

fun main(args: Array<String>) = runBlocking {
    val dispatcher = newSingleThreadContext("myDispatcher")
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("Error captured")
        println("Message: ${throwable.message}")
    }

    // Combine two contexts together
    val context = dispatcher + handler

    GlobalScope.launch(context) {
        println("Running in ${Thread.currentThread().name}")
        TODO("Not implemented!")
    }.join()
}

์ปจํ…์ŠคํŠธ ๋ถ„๋ฆฌ

๊ฒฐํ•ฉ๋œ ์ปจํ…์ŠคํŠธ์—์„œ ์ปจํ…์ŠคํŠธ ์š”์†Œ๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

fun main(args: Array<String>) = runBlocking {
    val dispatcher = newSingleThreadContext("myDispatcher")
    val handler = CoroutineExceptionHandler { _, throwable ->
        println("Error captured")
        println("Message: ${throwable.message}")
    }

    // Combine two contexts together
    val context = dispatcher + handler

    // Remove one element from the context
    val tmpCtx = context.minusKey(dispatcher.key)

    GlobalScope.launch(tmpCtx) {
        println("Running in ${Thread.currentThread().name}")
        TODO("Not implemented!")
    }.join()
}

์š”์•ฝ

  • withContext() ์ผ์‹œ ์ค‘๋‹จ ์ฝ”๋“œ ์ •์˜๋ฅผ ์œ„ํ•œ ์œ ์—ฐํ•œ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋น„๋™๊ธฐ ํ•จ์ˆ˜(job ๊ตฌํ˜„์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜)๋Š” ํŠน์ • ๊ตฌํ˜„์„ ๊ฐ•์š”ํ•˜๋Š” ์œ„ํ—˜์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด withContext() ๊ณต๊ฐœ API์˜ ์ผ๋ถ€๊ฐ€ ๋ผ์„œ๋Š” ์•ˆ ๋œ๋‹ค๊ณ  ์–ธ๊ธ‰ํ–ˆ๋‹ค.

  • ์ฝ”๋ฃจํ‹ด ์ปจํ…์ŠคํŠธ์— ๋Œ€ํ•œ ํฅ๋ฏธ๋กœ์šด ์ฃผ์ œ์™€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ดํ•ดํ•ด์•ผ ํ•œ๋‹ค.

  • ๋””์ŠคํŒจ์ฒ˜๋ฅผ ์‹œ์ž‘์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ์™€ ์ทจ์†Œ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ณ ์œ ํ•œ ์ปจํ…์ŠคํŠธ๋กœ ์˜ฎ๊ฒจ๊ฐ€๋Š” ๋‹ค์–‘ํ•œ ์œ ํ˜•์˜ ์ฝ”๋ฃจํ‹ด ์ปจํ…์ŠคํŠธ๊ฐ€ ์กด์žฌํ•œ๋‹ค.

  • ์ฝ”๋ฃจํ‹ด์—์„œ ๊ธฐ๋Œ€ํ•˜๋Š” ๋™์ž‘์„ ์–ป๊ธฐ ์œ„ํ•ด ๋งŽ์€ ์ปจํ…์ŠคํŠธ๋ฅผ ํ•˜๋‚˜๋กœ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ์š”์†Œ ์ค‘ ํ•˜๋‚˜์˜ ํ‚ค๋ฅผ ์ œ๊ฑฐํ•จ์œผ๋กœ์จ ๊ฒฐํ•ฉ๋œ ์ปจํ…์ŠคํŠธ๋ฅผ ๋ถ„๋ฆฌํ• ์ˆ˜ ์žˆ๋‹ค.

  • withContext()๋Š” ํ”„๋กœ์„ธ์Šค์— ์žก์„ ํฌํ•จ์‹œํ‚ค์ง€ ์•Š๊ณ ๋„ ๋‹ค๋ฅธ ์ปจํ…์ŠคํŠธ๋กœ ์ „ํ™˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ์ผ์‹œ ์ค‘๋‹จ ํ•จ์ˆ˜๋‹ค.

Last updated