GlobalScope은 프로젝트의 전반적인 구간을 의미합니다.
fun main() = runBlocking<Unit> {
GlobalScope.launch {
repeat(10) { i ->
println(" $i ...")
delay(500)
}
}
async {
println("do1() : "+do1())
}
println("...")
delay(5000)
}
suspend fun do1(): Int {
delay(1000)
// 스레드 블록이 아닌 코루틴을 중단하는 suspending function이므로 코루틴에서만 사용 가능
return 13
}
실행결과:
0 ...
...
1 ...
do1() : 13
2 ...
3 ...
4 ...
5 ...
6 ...
7 ...
8 ...
9 ...
위의 실행결과를 보면 알 수 있듯이 GlobalScope는 프로젝트가 종료될 때 까지 꾸준히 돌게되고 GlobalScope() 루틴 외부의 async 블럭과 println("...")이 순서대로 실행되는 것을 볼 수 있다.
취소
만약 코루틴이 실행되는 도중 사용자로 인해 페이지가 닫히면 그 결과를 얻기위해 코루틴이 계속 실행될 필요가 있을까? 더 이상 필요하지 않는 작업에 대해선 취소가 되어야 할텐데...
fun main() = runBlocking{
// 시작
val job = launch(Dispatchers.Default) {
var i = 0
while(i < 10) {
delay(500)
println("job...")
i++
}
}
println("...")
delay(3000)
println("main waiting.")
job.cancelAndJoin() // job을 취소하고 완료될 때 까지 대기
println("main bye.")
// 끝
}
실행결과:
...
job...
job...
job...
job...
job...
main waiting.
main bye.
위 코드를 보면 3초간 실행되고 Coroutine 블럭을 종료되도록 했다.
하지만 아래의 코드와 위 코드는 같은 의미의 코드지만 실행결과가 다르다. 코루틴 코드는 취소가 가능하도록 작성되어야 한다.
아래의 코드처럼 취소가 올바로 동작하지 못하는 코드가 작성될 수 도 있음에 유의해야한다.
fun main() = runBlocking{
// 시작
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while(i < 10) { //isActive
//yield()
if (System.currentTimeMillis() >= nextPrintTime) {
println("job...")
nextPrintTime += 500L
i++
}
}
}
println("...")
delay(3000)
println("main waiting.")
job.cancelAndJoin()
println("main bye.")
// 끝
}
실행결과:
...
job...
job...
job...
job...
job...
job...
job...
main waiting.
job...
job...
job...
main bye.
위 코드가 취소가 되지않아 여간 이해가 안되고 머리가 복잡해졌다. 하지만 yied() 함수는 해당 위치에서 코루틴을 일시 중단할 수 있고, 취소 여부를 확인하고 호출한 위치에서 취소가 가능해진다.
그리고 while()의 조건을 CoroutineScope의 확장 프로퍼티인 isActive로 변경하면 취소가 가능하다. isActive는 활성화 상태일 땐 true 그렇지 않을 떈 false를 반환하며, 기본값은 false 이다.
코루틴 마무리 작업
네트워크 또는 디스크를 다루는 경우 모든 작업이 정상 종료, 비정상 종료되면 리소스를 정리할 수 있도록 마무리 작업을 해주어야 한다.
try-finally문으로 마무리 : 코루틴 블록 내의 내용을 감싸주면, 작업이 종료될 시 finally 블럭이 실행된다.
fun main() = runBlocking{
// 시작
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
try {
var nextPrintTime = startTime
var i = 0
while(isActive) {
if (System.currentTimeMillis() >= nextPrintTime) {
println("job...")
nextPrintTime += 500L
i++
}
}
}finally {
println("job bye.")
}
}
println("...")
delay(3000)
println("main waiting.")
job.cancelAndJoin()
println("main bye.")
// 끝
}
실행결과:
...
job...
job...
job...
job...
job...
job...
job...
main waiting.
job: I'm running finally
main bye.
만약 finally 문에서 리소스 사용이 마무리 되기를 대기해야 한다면? delay()를 써서는 안된다. delay() 함수도 Coroutine 블럭이 취소되면 영향을 받는다. 따라서 finally 안에서는 kotlinx.coroutines 패키지의 함수들은 사용해선 안된다.
하지만 이러한 동작이 필요한 경우, withContext() 함수를 사용하면 된다.
finally {
withContext(NonCancellable) {
println("job : Wait 1 second.")
delay(1000L)
println("job bye.")
}
}
'Coroutine' 카테고리의 다른 글
Coroutines - Part. 5 안드로이드에서의 코루틴 - 동기식과 비동기식, 콜백과 코루틴 (0) | 2021.01.30 |
---|---|
Coroutines - Part. 4 안드로이드에서 코루틴 - ViewModel, Activity, Fragment (0) | 2021.01.30 |
Coroutines - Part. 3 withTimeout(), withTimeoutOrNull() (0) | 2021.01.30 |
Coroutines - Part 1. 완료 대기, 지연 실행 (0) | 2021.01.30 |
Coroutines - Part 1. Coroutine이란 (0) | 2021.01.30 |