[Kotlin] Coroutines 2

morning-la·2022년 11월 25일
0

kotlin

목록 보기
12/14

async 코루틴 빌더 생성

동시성 처리를 위한 async 코루틴

  • launch 와 다른점은 Deffered<T>를 통해 결과 값을 반환
  • 지원된 결과 값을 받기 위해 await()를 사용
private fun worksInParalled() {
	val one = GlobalScope.async { // 순차 실행과 무관하게 병행 처리
    	dowork1() // Deferred<T> 으로 결과 반환
    }
    val two = GlobalScope.async {
    	dowork2()
	}
    GlobalScope.launch {
    	val combined = one.await() + "_" + two.await() // 지연 함수
        println("Kotlin Combined : $combined")
    }
}

Coroutine Context

  • 코루틴을 실행하기 위한 다양한 설정값을 가진 관리 정보
    • 코루틴 이름, 디스패치, 작업 상세사항, 예외 핸들러 등
  • 디스패처는 코루틴 문맥을 보고 어떤 스레드에서 실행되고 있는지 식별이 가능해진다.
  • 코루틴 문맥은 + 연산을 통해 조합될 수 있다
val someCoroutineContext = someJob + Dispatchers.IO + someCoroutineName + someExceptionHandler

CoroutineName

  • 코루틴에 이점을 주며 디버깅을 위해서 사용됨
val someCoroutineName = CoroutineName("someCoroutineName")

Job

  • 작업 객체를 지정할 수 있으며 취소 가능 여부에 따라 SupervisorJob() 사용
val parentJob = SupervisorJob() // or Job()
val someJob = Job(parentJob)

CoroutineDispatcher

  • Dispatchers.Default, ... IO, 등을 지정할 수 있으며 필요에 따라 스레드풀 생성 가능
val mypool = Excutors.newFixedThreadPool(2).asCoroutineDispatcher()

코루틴 문맥

CoroutineExceptionHandler

  • 코루틴 문맥을 위한 예외처리를 담당하며 코루틴에서 예외가 던져지면 처리한다
  • 예외가 발생한 코루틴은 상위 코루틴에 전달되어 처리될 수 있다
    • 스코프를 가지는 경우 예외 에러를 잡아서 처리 할 수 있다
  • 만일 예외처리가 자식에게만 있곡 부모에 없는 경우 부모에도 예외가 전달되므로 주의
    • 이 경우 앱이 깨지게 됨
  • 예외가 다중으로 발생하면 최초 하나만 처리하고 나머지는 무시됨
val someExceptionHandler = CoroutineExceptionHandler { coroutineContext, throwalbe -> 
	val coroutineName = coroutineContext[coroutineName]?.name ?: "default coroutine name"
    println("Error in $coroutineName : ${throwable.localizedMessage}")
}

코루틴 스코프

GlobalScope

val scope = GlobalScope
scope.launch { ... }
scope.launch { ... }

or

GlobalScope.launch { ... }
val job1 = GlobalScope.launch { ... } // job 객체 -> job.joint() 으로 기다림
val job2 = GlobalScope.async { ... } // Deffered 객체 -> Deferred.await() 으로 기다림
  • 독립형 코루틴을 구성, 생명주기는 프로그램 전체에 해당하는 범위를 가지며 main의 생명 주기가 끝나면 같이 종료됨
  • Dispatchers.Unconfined와 함께 작업이 서로 무관한 전역 범위 실행
  • 보통 GlobalScope 상에서는 launch나 async 사용이 권장되지 않음

CoroutineScope

val scope2 = CoroutineScope(Dispatchers.Default)
val routine1 = scope1.launch { ... }
val routine2 = scope2.async { ... }

or

CoroutineScope(Dispatchers.Main).launch { ... }
launch(Dispatchers.Default) { ... }
async(Dispatchers.Default) { ... }
  • 특정 목적의 디스패처를 지정한 범위를 블록으로 구성할 수 있다
  • 모든 코루틴 빌더는 CoroutineScope 의 인스턴스를 갖는다
  • launch {..} 와 같이 인자가 없는 경우에는 coroutineScope에서 상위의 문맥이 상속되어 결정
  • launch(Dispatchers.옵션인자) {...} 와같이 디스패처의 스캐줄러를 지정 가능
    • Dispatchers.defaultglobalscope에서 실행되는 문맥과 동일하게 사용

스레드 풀

스레드 풀 사용

  • 보통 commonPool이 지정되어 코루틴이 사용할 스레드의 공동 풀을 사용
  • 이미 초기화되어 있는 스레드 중 하나 혹은 그 이상이 선택되며 초기화 하기 때문에
fun main() = runBlocking<Unit> {
	val request = launch {
    	GlobalScope.launch {
        	println("job1 : before suspend function")
            delay(1000)
            println("job1: after suspend function")
        }
        launch { // 부모 문맥을 상속. 상위 launch의 자식
        	delay(100)
            println("job2: before suspend function")
            delay(1000)
            println("job2: after suspend function") // req
        }
    }
    delay(500)
    request.cancel()
    delay(1000)
}

빌더의 특정 속성 지정

시작 시점에 대한 속성 - launch의 원형

public fun launch(
	context: CoroutineContext,
    start: CoroutineStart,
    parent: Job?
    onCompletion: CompletionHandler?,
    block: suspend CoroutineScope.() -> Unit):Job {
    ...
)

CoroutineStart

  • DEFAULT : 즉시 시작 (해당 문맥에 따라 즉시 스케줄링됨)
  • LAZY : 코루틴을 느리게 시작 (처음에는 중단된 상태이며 start()나 await() 등으로 시작)
  • ATOMIC : 원자적으로 즉시 시작 (DEFAULT와 비슷하나 코루틴을 실행전에는 취소 됨)
  • UNDISPATCHED : 현재 스레드에서 즉시 시작 (첫 지연함수까지 이후 재개시 디스 패치 됨)

start(), await()

  • launch나 async는 즉시 실행되지만 start 옵션에 따라 실행 시점을 늦출 수 있다
val job = async(start = CoroutineStart.LAZY) { dowork1() }
..
job.start() // 실제 시작 시점으로 또는 job.await()으로 시작됨

Reference

  • 코틀린 프로그래밍 - 황영덕
profile
Something New

0개의 댓글