본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[coroutine] coroutineScope 의 동작 특성을 알아보자.

by 돼지왕 왕돼지 2020. 3. 8.
반응형
[coroutine] coroutineScope 의 동작 특성을 알아보자.

 

-

아래 내용은 coroutine 에 대한 공부가 충분하지 않을 때 작성한 것이고, 22.10.13 기준 적당히 공부한 이후 한마디로 정리하면 아래와 같다.

더보기

coroutineScope 함수는 suspend function 으로 launch 등과 같은 coroutine builder 와는 다르다.
suspend function 이므로 당연히 해당 함수 호출이 return 되어야 다음 구문이 실행된다.

그럼에도 아래 실험 결과들은 한번쯤 들여다볼만하다고 생각된다.

 

 

-

Coroutine 공식 가이드 문서에 있는 아래 샘플 결과를 보고 약간 멘붕에 빠졌다.

그래서 관련하여 실험한 결과를 여기에 정리해보고자 한다.

fun main() = runBlocking{
	launch{
    	delay(200L)
        println("Task from runBlocking")
    }
    
    coroutineScope{
    	launch{
        	delay(500L)
            println("Task from nested launch")
        }
    	delay(100L)
        println("Task from coroutine scope")
    }
    
    println("Coroutine scope is over") 
} 

// Task from coroutine scope 
// Task from runBlocking 
// Task from nested launch 
// Coroutine scope is over

 

나의 의문은.. 왜 Coroutine scope is over 가 마지막에 불리는가였다.

예상한 결과는 Task from coroutine scope 다음에 불렸어야 된다였는데..

 

 

-

그래서 다음과 같이 point 들의 로그를 찍어보았고, println 을 overload 하여 thread 도 함께 로그로 찍었다.

fun main() =runBlocking { 
    launch { 
        println("point 1")
        delay (200L) 
        println ("Task from runBlocking")
    }
    
    println ("point 2") 
    
    coroutineScope { 
        println("point 3")
        
        launch { 
            println("point 4") 
            delay (500L) 
            println ("Task from nested launch")
        }
        
        println ("point 5") 
        
        delay (100L) 
        
        println ("Task from coroutine scope")
    } 

    println ("Coroutine scope is over") 
}

 

 

어떤 결과가 예상되는가? 

결과를 보려면 "더보기" 를 눌러보자.

더보기

// Thread[main,5,main] / point 2

// Thread[main,5,main] / point 3

// Thread[main,5,main] / point 5

// Thread[main,5,main] / point 1

// Thread[main,5,main] / point 4

// Thread[main,5,main] / Task from coroutine scope

// Thread[main,5,main] / Task from runBlocking

// Thread[main,5,main] / Task from nested launch

// Thread[main,5,main] / Coroutine scope is over

 

해석을 해보자면.. 

coroutineScope{ ... } 구문은 thread 전환이나 끊김이 없이 곧 바로 code block 을 호출한다.

그리고 launch 는 handler.post 처럼 thread 점유를 기다리게 된다.

하지만.. 가장 신기한 점은.. coroutineScope{ ... } 의 블록의 "이탈"은 그 안의 모든 child 의 complete 후였다.

나의 이해 범주에서는 coroutineScope 의 "종료" 는 모든 child 의 complete 이지만, "thread 이탈"이 child complete 후라는 것은 조금 충격.. (  println("Coroutine scope is over") 코드로 넘어가는 것이 coroutineScope 의 종료 후라는 말 )

 

 

-

그래서 이것이 runBlocking 과의 조합 때문에 다른 결과가 나오는 것일까? 라는 의문을 갖고 다음과 같이 테스트해보았다.

fun main() = GlobalScope.launch {
    launch {
        println("point 1")
        delay(200L)
        println("Task from runBlocking")
    }

    println("point 2")
    
    coroutineScope {
        println("point 3")
        
        launch {
            println("point 4")
            delay(500L)
            println("Task from nested launch")
        }
        
        println("point 5")
        delay(100L)
        println("Task from coroutine scope")
    }
    
    println("Coroutine scope is over")
}
 
결과는? (더보기를 눌러주세요)

 

더보기

// Thread[DefaultDispatcher-worker-1,5,main] / point 2

// Thread[DefaultDispatcher-worker-2,5,main] / point 1

// Thread[DefaultDispatcher-worker-1,5,main] / point 3

// Thread[DefaultDispatcher-worker-1,5,main] / point 5

// Thread[DefaultDispatcher-worker-3,5,main] / point 4

// Thread[DefaultDispatcher-worker-2,5,main] / Task from coroutine scope

// Thread[DefaultDispatcher-worker-4,5,main] / Task from runBlocking

// Thread[DefaultDispatcher-worker-4,5,main] / Task from nested launch

// Thread[DefaultDispatcher-worker-4,5,main] / Coroutine scope is over

 

위 결과의 point 순서는 실행 할 때마다 조금씩 다르게 나온다.

그러나 핵심은 이렇다. delay 를 마딱뜨리기 전인 point2, point3, point 5 를 찍는 thread 는 worker-1 로 동일하며, 이 들의 호출 순서는 보장된다.

그리고 Task from coroutine scope 를 찍는 thread 는 worker-1 이 아닌 worker-2 를 사용한다. (resume 시의 thread 전환 가능이 coroutine 의 또 다른 장점이라지?)

그리고 마지막 Coroutine scope is over 는 마찬가지로 coroutineScope { ... } 구문이 완료된 후에 불린다.

 

 

-

최종 결론은 coroutineScope{ ... } block 을 out 하려면 child 가 모두 종료되어야 한다!!!!

 

더보기

coroutinescope, coroutinescope behavior, coroutinescope block, coroutinescope builder, coroutinescope test, globalscope thread, [coroutine] coroutineScope 의 동작 특성을 알아보자.

 

반응형

댓글