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