기능 | 설명 |
---|---|
launch, async | 코루틴 빌더 |
Job, Deferred | cancellation 지원을 위한 기능 (Job은 생명주기를 갖고 동작하는 작업의 단위) |
Dispatchers | 일종의 스케줄러, Default는 백그라운드 코루틴을 위해, Main은 Android, Swing, JavaFX를 위해 사용됨. |
delay, yield | 상위 레벨 지연 (suspending) 함수 |
Channel, Mutex | 통신과 동기화를 위한 기능 |
coroutineScope, supervisorScope | 범위 빌더 |
select | 표현식 지원 |
기능 | 설명 |
---|---|
CommonPool | 코루틴 컨텍스트 |
produce, actor | 코루틴 빌더 |
📌 코루틴 라이브러리 추가하기File > Project Structures > Libraries > From Maven > org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:version 추가cf) https://stackoverflow.com/questions/52522282/using-kotlinx-coroutines-in-intellij-idea-project
package com.hero
import kotlinx.coroutines.*
fun main() { // 메인 스레드의 컨텍스트
GlobalScope.launch { // 새로운 코루틴을 백그라운드에서 실행
delay(1000L) // 1초 넌블로킹 지연
print("World!") // 지연 후 출력
}
print("Hello, ") // 코루틴이 지연되는 동안 메인 스레드는 계속 실행
Thread.sleep(2000L) // 메인 스레드가 JVM에 의해 바로 종료되지 않도록 2초 지연
}
Hello, World! (Hello, 가 출력되고 1초 뒤에 World! 가 출력됨.)
- 코루틴을 생성하면, 새로운 루틴이 하나 더 생성된다고 보면 된다.
- delay()와 같은 suspend 함수는 반드시 코루틴 안에서 사용해야 한다.
- suspend 함수 중 하나인 delay()는 일시 중단될 수 있으며 필요한 경우 재개된다. 일시 중단되었을 때, 다른 스레드는 블로킹 되지 않고 계속 실행된다. (넌블로킹)
- 반면에, sleep() 함수는 메인 스레드를 블록킹 시킨다. 위의 코드에서 GlobalScope로 지정된 launch 코루틴의 수행 완료를 기다리지 않으면, 프로그램이 종료되어 코루틴도 같이 사라지게 된다. (코루틴은 프로그램과 생명주기를 같이 함.) 따라서, sleep() 함수로 메인 스레드를 블로킹 시켜야 delay() 함수의 그 다음 부분도 실행될 수 있다.
public suspend fun delay(timeMills: kotlin.Long): kotlin.Unit { / compiled code / }
package com.hero
import kotlinx.coroutines.*
fun main() { // 메인 스레드의 컨텍스트
//doSomething() // error
GlobalScope.launch { // 새로운 코루틴을 백그라운드에서 실행
delay(1000L) // 1초 넌블로킹 지연
print("World!") // 지연 후 출력
doSomething() // ok
}
print("Hello, ") // 코루틴이 지연되는 동안 메인 스레드는 계속 실행
Thread.sleep(2000L) // 메인 스레드가 JVM에 의해 바로 종료되지 않도록 2초 지연
}
suspend fun doSomething() {
println("Do something!")
}
package com.hero
import kotlinx.coroutines.*
fun main() {
val job = GlobalScope.launch {
delay(1000L)
println("World")
doSomething()
}
println("Hello")
println("job: $job")
Thread.sleep(2000L)
println("job: $job")
}
suspend fun doSomething() {
println("Do something")
}
Hellojob: StandaloneCoroutine{Active}@5b1d2887WorldDo somethingjob: StandaloneCoroutine{Completed}@5b1d2887
package com.hero
import kotlinx.coroutines.*
fun main() {
val job = GlobalScope.launch {
delay(1000L)
println("World")
doSomething()
}
println("Hello")
println("job: ${job.isActive}, ${job.isCompleted}")
Thread.sleep(2000L)
println("job: ${job.isActive}, ${job.isCompleted}")
}
suspend fun doSomething() {
println("Do something")
}
Hellojob: true, falseWorldDo somethingjob: false, true
- Job 객체는 코루틴의 생명주기를 관리하며, 생성된 코루틴 작업들은 부모-자식 같은 관계를 가질 수 있다.
- 부모가 취소(cancel)되거나 실행에 실패하면, 그 하위 자식들도 모두 취소된다.
- 반대로, 자식이 실행에 실패할 때도 그 부모와 다른 자식들 모두 취소된다.
- 단, SupervisorJob의 경우 자식이 실행에 실패하더라도 그 부모나 다른 자식에 영향을 주지 않아서 실행을 유지할 수 있다.
package com.hero
import kotlinx.coroutines.*
fun main() {
runBlocking {
val job = GlobalScope.launch {
delay(1000L)
println("World")
doSomething()
}
println("Hello")
println("job: ${job.isActive}, ${job.isCompleted}")
//Thread.sleep(2000L)
job.join()
println("job: ${job.isActive}, ${job.isCompleted}")
}
}
suspend fun doSomething() {
println("Do something")
}
Hellojob: true, falseWorldDo somethingjob: false, true
package com.hero
import kotlinx.coroutines.*
fun main() {
runBlocking {
val job = GlobalScope.launch {
delay(1000L)
println("World")
doSomething()
}
println("Hello")
println("job: ${job.isActive}, ${job.isCompleted}")
//Thread.sleep(2000L)
//job.join()
println("job: ${job.isActive}, ${job.isCompleted}")
}
}
suspend fun doSomething() {
println("Do something")
}
Hellojob: true, falsejob: true, false
join()을 해주지 않으면 코루틴의 작업이 완료될 때까지 기다리지 않고, 메인 스레드가 계속 실행되기 때문에 위와 같은 결과가 나오고 프로그램이 종료된다.
package com.hero
import kotlinx.coroutines.*
suspend fun doWork1(): String {
delay(1000)
return "Work1"
}
suspend fun doWork2(): String {
delay(3000)
return "Work2"
}
private fun workInSerial(){
GlobalScope.launch {
// 코루틴 내의 suspend 함수는 순차적으로 실행됨.
val one = doWork1()
val two = doWork2()
// 1초, 3초 지나고나서 출력
println("Kotlin one: $one")
println("Kotlin two: $two")
}
}
fun main() {
workInSerial()
readLine() // 메인 스레드가 먼저 종료되는 것을 막기 위해 콘솔에서 입력 대기
}
package com.hero
import kotlinx.coroutines.*
suspend fun doWork1(): String {
delay(1000)
return "Work1"
}
suspend fun doWork2(): String {
delay(3000)
return "Work2"
}
private fun workInSerial(): Job {
val job = GlobalScope.launch {
// 코루틴 내의 suspend 함수는 순차적으로 실행됨.
val one = doWork1()
val two = doWork2()
// 1초, 3초 지나고나서 출력
println("Kotlin one: $one")
println("Kotlin two: $two")
}
return job
}
fun main() {
runBlocking {
val job = workInSerial()
job.join()
}
//readLine()
}
Kotlin one: Work1Kotlin two: Work2
package com.hero
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
suspend fun doWork1(): String {
delay(1000)
return "Work1"
}
suspend fun doWork2(): String {
delay(3000)
return "Work2"
}
private fun workInSerial(): Job {
val job = GlobalScope.launch {
// 코루틴 내의 suspend 함수는 순차적으로 실행됨.
val one = doWork1()
val two = doWork2()
// 1초, 3초 지나고 나서 출력
println("Kotlin one: $one")
println("Kotlin two: $two")
}
return job
}
fun main() {
runBlocking {
val time = measureTimeMillis {
val job = workInSerial()
job.join()
}
println("time: $time")
}
}
Kotlin one: Work1Kotlin two: Work2time: 4077
실행 시간을 보면, doWork1()과 doWork2()가 순차적으로 실행되었다는 걸 알 수 있다. 그렇다면, 특정 suspend 함수들을 비동기적으로 동시에 실행할 수는 없을까?
package com.hero
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
suspend fun doWork1(): String {
delay(1000)
return "Work1"
}
suspend fun doWork2(): String {
delay(3000)
return "Work2"
}
private fun workInSerial(): Job {
val job = GlobalScope.launch {
// 코루틴 내의 suspend 함수는 순차적으로 실행됨.
val one = doWork1()
val two = doWork2()
// 1초, 3초 지나고 나서 출력
println("Kotlin one: $one")
println("Kotlin two: $two")
}
return job
}
private fun workInParallel(): Job {
val one = GlobalScope.async {
doWork1()
}
val two = GlobalScope.async {
doWork2()
}
val job = GlobalScope.launch {
// one, two가 동시에 실행됨.
val combined = one.await() + "_" + two.await()
println("Kotlin Combined: $combined")
}
return job
}
fun main() {
runBlocking {
val time = measureTimeMillis {
val job = workInParallel()
job.join()
}
println("time: $time")
}
}
Kotlin Combined: Work1_Work2time: 3080
doWork1()과 doWork2()가 비동기적으로 동시에 실행되어서 실행 시간이 3초 정도로 나온 것을 볼 수 있다.