Basics

Yes. Dominos~·2021년 10월 12일
0

새차원의코루틴

목록 보기
2/2
post-thumbnail

이 글은 유튜브 강의인 새차원의 코틀린 코루틴을 보고 정리하여 작성한 글입니다.
https://www.youtube.com/playlist?list=PLbJr8hAHHCP5N6Lsot8SAnC28SoxwAU5A

이제 본격적으로 코틀린 Code 를 직접 작성하며 이해하면서 진행하게 되는데, Inteli J IDE 를 이용하여 작업을 한다.

인텔리J(IntelliJ) 코틀린(Kotlin) 프로젝트 생성과 Hello Kotlin 출력(#2)
https://bestpractice80.tistory.com/68

위의 블로그를 통해 Inteli J IDE 를 이용하여 코틀린 프로젝트를 처음으로 생성하고 실행해 보았다.
참고하기 바란다.



1. Your first coroutine

fun main(args: Array<String>){

    GlobalScope.launch {  // 새로운 코루틴을 백그라운드에서 실행시킨다.
        delay(1000L) 	  // 1초 딜레이
        println("World!") // "World!" 콘솔 프린트
    }
    println("Hello,") 	// "Hello," 콘솔 프린트
    Thread.sleep(2000L) // 메인 스레드 2초간 정지
}

결과가 어떻게 나올것 같은가?
스레드에 대한 이해가 없다??? 그럼 지금 이 글을 보고 있을때가 아니다.(스레드를 이해한 뒤에 코루틴을 배우는 것이 좋다.)

결과값 :
(GlobalScope 1초 딜레이 시작)
Hello,
(메인스레드 2초 정지 시작)
(1초후)
World!
(메인스레드 2초 정지 끝)
=> 메인스레드와 코루틴이 거의 동시에 실행된다.

처음으로 코루틴을 사용해 보았을 텐데
추후에 GlobalScope , CoroutineScope 에 대한 내용을 다룰 것이다.
GlobalScope.launch 에서 launch 는 해당 GlobalScope 를 실행(build) 시키겠다는 의미 이다.



2. Bridging blocking and non-blocking worlds

위의 1번 예제 에서는 Thread.sleep(2000L) 을 통해서 메인스레드를 2초간 정지 시켰었다.

1번 예제에서 GlobalScope 내에서는 delay 함수를 사용하였고,
Main 함수 내에서는 Thread.sleep 을 사용하여 일시중지 시켰었다.
이번에는 delay 만을 사용하여 만들어 보자는 것이다.

그런데 delay 함수는 suspend 함수(코루틴에서 일시적으로 빠져나가는)여서 코루틴과 함께 사용되어야 한다.
그래서 blocking(=코드진행 중단) 을 위한 코루틴을 사용하게 되는데 = runBlocking 이다.

fun main(args: Array<String>){

    GlobalScope.launch {  // 새로운 코루틴을 백그라운드에서 실행시킨다.
        delay(1000L) 	  // 1초 딜레이
        println("World!") // "World!" 콘솔 프린트
    }
    println("Hello,") 	// "Hello," 콘솔 프린트
    runBlocking {
    	delay(2000L) 
    } // Blocking 이기때문에 다음 코드 진행 중단.
}

결과값 :
(GlobalScope 1초 딜레이 시작)
Hello,
(메인스레드 2초 정지 시작)
(1초후)
World!
(메인스레드 2초 정지 끝)



3. rewritten in a more idiomatic way

2번 예제를 보면, 어찌되었든 GlobalScope 내부의 내용이 출력되기 전까지는 메인스레드가 종료되지 않게끔 작성되었다.
그래서 간결하고 이해하기 좀더 쉬운 관용적인(일반적으로 사용하는) 표현으로 코드를 재작성 해보라고 한다.

fun main() = runBlocking { // runBlocking 으로 전체를 감싸 안았다.
    GlobalScope.launch {  
        delay(1000L) 	  
        println("World!") 
    }
    println("Hello,") 	
    delay(2000L) 
}

결과값 :
(GlobalScope 1초 딜레이 시작)
Hello,
(메인스레드 2초 정지 시작)
(1초후)
World!
(메인스레드 2초 정지 끝)



4. Waiting for a job

이전에는 delay를 사용하여 대기하였는데, 대신에 job 을 사용해보자.

3번 예제를 보면, 코드 마지막줄의 delay(2000L) 를 통해 GlobalScope의 World! 가 찍히게끔 할수 있다.
하지만 GlobalScope 의 delay(3000L) 3초 이면 World! 가 찍히겠는가?
메인스레드가 종료되었기 때문에 찍히지 않을 것이다.
그래서 delay 를 이용하는 방법은 그렇게 좋은 방법은 아니다.

fun main() = runBlocking {
    val job = GlobalScope.launch {  
        delay(3000L) 	  
        println("World!") 
    }
    println("Hello,") 	
    job.join()	// job 코루틴이 끝날때까지 기다렸다가, 메인스레드를 종료시킨다.
}

결과값 :
(GlobalScope 3초 딜레이 시작)
Hello,
(3초후)
World!
( job.join() 으로 인해 코루틴 종료후 메인스레드 끝)



5. Structured concurrency

4번의 job 방식보다 좋은 솔루션

5-1. GlobalScope.launch 방식을 이용하여 계속 코루틴을 사용했는데, 이것을 top-level-coroutine 이라고 한다.
5-2. GlobalScope 은 일정량 이상의 메모리를 계속 점유하고
5-3. join 을 꼭 써줘야지만, 코루틴이 끝날때 까지 기다린다.(코루틴 10개 있으면 10개의 join을 써줘야함??? 헐..)

fun main() = runBlocking { // this: CoroutineScope
    launch {  // this: CoroutineScope
        delay(1000L) 	  
        println("World!") 
    }
    
    launch {  // this: CoroutineScope
        delay(2000L) 	  
        println("Man~") 
    }
    
    println("Hello,") 	
}

결과값 :
Hello,
World!
Man~

GlobalScope 사용하지말고~
runBlocking 이 코루틴 이니까
코루틴은 자식 코루틴을 여러개 생성할수 있는데,
부모 코루틴이 자식 코루틴이 모두 종료될때 까지 굳이 join 같은것 써주지 않아도 기다려 준다 이말이다.



6. Extract function refactoring

Suspend 함수를 사용해보자!

fun main() = runBlocking { // this: CoroutineScope
    launch { 
    	doWorld() // suspend 함수에 코루틴이 접근하게 되면 다음 코드 진행을 멈춘다.
    }
    println("Hello,") 
}

suspend fun doWorld() {
    delay(1000L) 	  
    println("World!") 
}

suspend 함수를 접근하게 되면
서브 코루틴의 진행을 잠시 멈추고 suspend 함수 내용을 진행 시킨다.

결과값 :
Hello,
(1초후)
World!



7. Coroutines ARE light-weight

코루틴은 엄청 가볍다라는 것을 보여주는 예제
10만개의 코루틴을 실행시켜본다.

fun main() = runBlocking { // this: CoroutineScope
    repeat(100_000) {
    	launch { 
        	delay(1000L)
            print(".")
        }
    }
}

이걸 thread로 바꾸서 하면??
= 무겁다.. 컨트롤이 안된다.
코루틴이 구조적으로 엄청 가볍다는 것이다.



# 8. Global coroutine are like daemon threads

GlobalScope 코루틴은 마치 데몬스레드 같다.
suepend <-> resume 체험

fun main() = runBlocking {    
    launch {
    	repeat(5) { i ->	// 5번 실행
        	println("Coroutine A, $i")
        }    
    }
    
    launch {        
    	repeat(5) { i ->	// 5번 실행
        	println("Coroutine B, $i")
        }
    }
    
    println("Coroutine Outer")
}

영상 참고



정리

  1. Coroutine builder : 코루틴을 실행시키기 위한 builder
    1-1. launch
    1-2. runBlocking

  2. Scope : 코루틴 범위 지정
    2-1. CoroutineScope
    2-2. GlobalScope

  3. Suspend function : 코루틴 진행을 잠시 벗어나는 함수
    3-1. suspend
    3-2. delay()
    3-3. join()

Structured concurrency : 위에서 보았던 join, job 같은 것들을 관리하지 않아도 알아서 서브코루틴을 기다려준다는 것.

profile
미래의 아들아~ 곧 만나러 갈게

0개의 댓글