Language/Kotlin

[Coroutine] Coroutine builder: 코루틴 시작

개랭갱깽스타 2022. 2. 7. 10:16

Coroutine builder

코루틴 빌더에 원하는 동작을 람다로 넘긴 후 → 코루틴을 만들어 실행한다.

 

🙉   Job , Defferd<T> 는 뭐지?

더보기

코루틴 작업을 다루는 객체 (오브젝트)

  • Job, Defferd<T>에 대해 취소나 예외처리를 함으로써 용이하게 코루틴의 흐름제어를 할 수 있게 된다.
  • Deferred<T> 인터페이스는 Job인터페이스를 상속 받는다.

 

더 자세한 내용은 이전글을 참고해주세요! 

2022.02.06 - [Platform/Android] - [Coroutine] Coroutine Context: 코루틴이 실행되는 곳

 

🙉   SendChannel<E>, ReceiveChannel<E> 는 뭐지?

더보기

둘 다 Channel!

  • stream을 반환한다.
  • FIFO 방식의 queue로 되어 있다.
  • 더 이상 사용하지 않을때 close 할 수 있다.
    • close는 특별한 token의 형태 → 이 값도 channel에 들어간다. → 따라서 iterator들은 이 값을 만나면(receive) iteration을 멈춥니다.
    • 이런 형태이기 때문에 close를 호출하더라도 그 이전에 넣어놓은 값들을 받아올 수 있음을 보장한다.

 

따라서, 동시성이 필요한 여러 Coroutine 에서 순서를 보장 받으면서 공유하여 사용할 수 있다!

 

더 자세한 내용은 아래 블로그를 참고해주세요! 

https://tourspace.tistory.com/156?category=797357

 


 

launch

현재 스레드를 Blocking 하지 않고, 새로운 코루틴을 실행한다.

  • Job 으로 반환한다. 즉, 리턴 값이 없는 코루틴을 위해 사용한다.
  • CoroutineScope 의 확장함수

 

  • API
    public fun CoroutineScope.launch(
    	    context: CoroutineContext = EmptyCoroutineContext,//CoroutineContext 기본
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> Unit
    ): Job​
  • 더보기
    public fun CoroutineScope.launch(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> Unit
    ): Job {
        val newContext =newCoroutineContext(context)
        val coroutine = if (start.isLazy)
            LazyStandaloneCoroutine(newContext, block) else
            StandaloneCoroutine(newContext, active = true)
        coroutine.start(start, coroutine, block)
        return coroutine
    }

 


 

async

현재 스레드를 Blocking 하지 않고, 새로운 코루틴을 실행한다.

  • Deffered<T>로 반환한다. 즉, 리턴 값이 있는 코루틴을 위해 사용한다.
  • 결과값을 반환 받고 싶은 경우, Aysnc Builder를 사용한다. - await()
  • CoroutineScope 의 확장함수

 

  • API
    public fun <T> CoroutineScope.async(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> T
    ): Deferred<T>
  • 더보기
    public fun <T> CoroutineScope.async(
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> T
    ): Deferred<T> {
        val newContext = newCoroutineContext(context)
        val coroutine = if (start.isLazy)
            LazyDeferredCoroutine(newContext, block) else
            DeferredCoroutine<T>(newContext, active = true)
        coroutine.start(start, coroutine, block)
        return coroutine
    }

 


 

withContext

부모 코루틴에 사용되던 context 와 다른 context 에서 코루틴을 실행시킬 수 있다. (Dispatchers이용)

  • T를 반환한다.
  • 결과 리턴이 나올때까지 대기한다.(이 자체로 정지 함수이다.)
    • 마지막 줄의 값이 반환값이 된다.
  • 비동기 작업을 순차 코드처럼 작성할 수 있게 된다.

 

  • API
    public suspend fun <T> withContext(
        context: CoroutineContext,
        block: suspend CoroutineScope.() -> T
    ): T

 

  • 예제
  • val scope = CoroutineScope(Dispatchers.Main)
    
    fun login()= scope.launch {
    	view.showLoading()
    	withContext(Dispatcher.IO) { networkClient.login(...)}
    	view.hideLoading()
    }

 


 

runBlocking

현재 스레드를 Blocking 하고, 새로운 코루틴을 실행한다.

  • T를 반환한다.
  • 사용하지 말라고 권장한다. → 특히, 안드로이드에서는 ANR 이 발생할 위험이 있다!
  • 코드 테스트, 레거시 코드, 라이브러리 통합 시에만 사용하는 것이 좋다.

 

  • API
  • public fun <T> runBlocking(
    		context: CoroutineContext = EmptyCoroutineContext, 
    		block: suspend CoroutineScope.() -> T): T

 

  • 예제
  • fun runBlockingExample() {
        runBlocking {   //일반함수
            launch {    //CoroutineScope 의 확장함수
                log("GlobalScope.launch started")
            }
        }
    }​
    fun main() {
        log("main() started")
        runBlockingExample()
        log("runBlockingExample() executed")
        log("main() terminated")
    }​
  • 더보기

    결과

    22:26:31.361 : Thread[main,5,main] : main() started
    22:26:31.448 : Thread[main,5,main] : GlobalScope.launch started
    22:26:31.448 : Thread[main,5,**main**] : runBlockingExample() executed
    22:26:31.448 : Thread[main,5,main] : main() terminated #일시정지 없음

 


 

actor

Channel을 통해 코루틴 블럭(Scope)와 외부에서 통신을 통해 전송, 처리의 루틴을 실행한다.

  • SendChannel<E> 를 반환한다.
    • 해당 코루틴을 사용하는 곳에서 send(E) 형태로 E (메시지) 를 보내면
    • 해당 코루틴에서 channel 내 메시지 E 로 받을 수 있다.
  • actor{ } 블록 내부는 수신자(Receiver)가 되고 / 반환된 SendChannel이 송신자(Sender)가 된다.
  • 상태 엑세스를 단일 thread로 한정한다. / 다른 thread는 채널을 통해서 상태 수정을 요청한다.
    • 동기화 이슈가 있는 자원을 actor 내에서 관리하도록 한다.

 

  • API
  • public fun <E> CoroutineScope.actor(
        context: CoroutineContext = EmptyCoroutineContext,
        capacity: Int = 0, // todo: Maybe Channel.DEFAULT here?
        start: CoroutineStart = CoroutineStart.DEFAULT,
        onCompletion: CompletionHandler? = null,
        block: suspend ActorScope<E>.() -> Unit
    ): SendChannel<E>​
  • 더보기
    public fun <E> CoroutineScope.actor(
        context: CoroutineContext = EmptyCoroutineContext,
        capacity: Int = 0, // todo: Maybe Channel.DEFAULT here?
        start: CoroutineStart = CoroutineStart.DEFAULT,
        onCompletion: CompletionHandler? = null,
        block: suspend ActorScope<E>.() -> Unit
    ): SendChannel<E>
     

 

produce

Channel을 통해 코루틴 블럭(Scope)와 외부에서 통신을 통해 전송, 처리의 루틴을 실행한다.

  • ReceiveChannel<E>를 반환한다.
  • produce{ } 블록 내부는 송신자(Sender)가 되고, 반환된 ReceiveChannel이 수신자(Receiver).
  • producer 는 값이 생성된 후 일시 중단되며, 새로운 값이 요청될 때 다시 재개한다. (시퀀스, 이터레이터와 유사)
  • 특정 Coroutine Scope 로 생성할 수 있다.
  • 전달되는 람다는 언제든지 일시 중단될 수 있다.
  • Channel을 사용해 작동 → 데이터를 스트림처럼 생각할 수 있다. 요소를 수신하면 스트림에서 요소가 제거된다.

 

  • API
  • public fun <E> CoroutineScope.produce(
        context: CoroutineContext = EmptyCoroutineContext,
        capacity: Int = 0,
        @BuilderInference block: suspend ProducerScope<E>.() -> Unit
    ): ReceiveChannel<E>
  • 더보기
    public fun <E> CoroutineScope.produce(
        context: CoroutineContext = EmptyCoroutineContext,
        capacity: Int = 0,
        @BuilderInference block: suspend ProducerScope<E>.() -> Unit
    ): ReceiveChannel<E> {
        val channel = Channel<E>(capacity)
        val newContext = newCoroutineContext(context)
        val coroutine = ProducerCoroutine(newContext, channel)
        coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
        return coroutine
    }

 

  • 예제
  • fun CoroutineScope.produceSquares(): ReceiveChannel<Int> = produce {
            for (x in 1..5) send(x * x)
        }
    
        runBlocking {
            val squares = produceSquares()
            squares.consumeEach { log("📗","consumeEach, $it") }
            log("📙", "Done!")
        }
  • 더보기

    결과

 

.참고

https://tourspace.tistory.com/156?category=797357

https://myungpyo.medium.com/코루틴-공식-가이드-자세히-읽기-part-8-1b434772a100

https://origogi.github.io/coroutine/액터/

반응형