Corouties
매우 작고 가벼운 쓰레드 - 수천, 수백개가 동시에 동작해도 no overhead! no crash! 강력함
동기화와 병렬처리를 쉽게 구현가능함
쉬운 표현으로 학습이 쉬움
언제나 일시정지하고 정지시킬 수 있음
CoroutineContext는 coroutine이 실행 중인 여러 job과 dispatcher를 저장하는 일종의 맵
launch, async, runBlocking과 같은 코루틴 빌더가 존재
delay(), yield(), withContext, withTimeout, awaitAll, joinAll 등과 같은 일시중단(suspending) 함수가 존재
*일시중단 함수는 coroutine이나 일시중단 함수 아닌 곳에서 호출금지
Scope, Context, Suspending functions, Jobs, Deferred, dispatcher, error handling
Scope
수명주기 방법을 제공하고 핵심 루틴을 관리
fun main(){
println("Program execution will now block")
runBlocking {
launch {
delay(1000L)
println("Task from runBlocking")
}
GlobalScope.launch {
delay(500L)
println("Task from GlobalScope")
}
coroutineScope {
launch {
delay(1500L)
println("Task from coroutineScope")
}
}
}
println("Program execution will now continue")
}
GlobalScope.launch {}
전체 응용프로그램 범위의 Coroutine 생성
전체 응용프로그램이 중지되지 않으면 계속 실행 (프로그램 중지되면 종료), 넓은 범위라 사용빈도 거의 없음
fun main(){
GlobalScope.launch {
delay(1000)
println("HELLO")
}
print("HELLO, ")
//Thread.sleep(5000) main을 coroutine 보다 지연시키지 않으면 coroutine 실행안됨
}
runBlocking{ }
scope를 생성 후 coroutine이 종료될 때 까지 다른 스레드 실행을 중지
일반함수로 coroutine scope없이 사용 가능
fun main(){
runBlocking {
launch {
delay(1000)
println("HELLO")
}
}
print("HELLO, ")
}
coroutineScope{}
모든 coroutine이 완료될때까지 완료되지 않는 새로운 scope를 생성할 때 사용
Context
coroutine과 관계된 데이터 셋
fun main(){
runBlocking {
launch(CoroutineName("myCoroutine")) {
println("This is run from ${this.coroutineContext.get(CoroutineName.Key)}")
}
}
}
context 2가지 요소
Dispatcher - 각 coroutine을 실행
Job - coroutine의 생명주기를 조절
Suspending functions
coroutine에서 호출 가능한 일시중단 함수로 지정
callback을 위해 사용
var functionCalls = 0
fun main(){
GlobalScope.launch { completeMessage() }
GlobalScope.launch { improveMessage() }
print("begin, ")
Thread.sleep(2000L)
println("function Count: $functionCalls")
}
suspend fun completeMessage(){
delay(500L)
println("Completed")
functionCalls++
}
suspend fun improveMessage(){
delay(1000L)
println("improve")
functionCalls++
}
Jobs
.launch() 호출을 통해 작업 반환
수명주기를 조작하는 것을 허용
부모 또는 자식 양쪽에 다른 작업의 계층에 존재
기존 수명주기에 변수나 메서드를 취소하거나 추가할 수 있음 - cancel(), join()
작업을 종료하면 모든 부모와 자식이 취소 됨
fun main(){
runBlocking {
val job1 = launch {
//delay(3000L)
println("job1 start")
val job2 = launch {
println("job2 start")
delay(3000L)
println("job2 end")
}
job2.invokeOnCompletion { println("job2 completed") }
val job3 = launch {
println("job3 start")
delay(3000L)
println("job3 end")
}
job3.invokeOnCompletion { println("job3 completed") }
}
job1.invokeOnCompletion {//job1이 종료되는 시점에 동작
println("job1 completed")
}
delay(500L)
println("job1 cancelled")
job1.cancel()
}
}
Dispatchers
coroutine이 실행되는 스레드 또는 스레드풀을 결정
구현해야할 경우가 아니면 따로 정의하지 않아도 됨
Main
UI 업데이트
Default
데이터 또는 이미지 처리와 같은 CPU 작업
IO
모든 입출력 작업
Unconfined
상속받은 Dispatcher에서 coroutine이 호출된 경우
newSingleThreadContext("Thread Name")
일시적으로 사용할 Thread 생성
fun main(){
runBlocking {
/*launch(Dispatchers.Main){
println("Main: ${Thread.currentThread().name}")
}*/
launch(Dispatchers.Unconfined){
println("Unconfined: ${Thread.currentThread().name}")
delay(100L)
println("move Unconfined: ${Thread.currentThread().name}")
}
launch(Dispatchers.Default){
println("Default: ${Thread.currentThread().name}")
delay(100L)
println("move Default: ${Thread.currentThread().name}")
}
launch(Dispatchers.IO){
println("IO: ${Thread.currentThread().name}")
}
launch(newSingleThreadContext("ThreadName")){
println("newSingleThreadContext: ${Thread.currentThread().name}")
}
}
}
Async
지연을 통해서 특정시점에 실행되도록 지정
Deffered를 반환 (.launch()로 실행할 경우 job을 반환)
.await()을 통해 결과가 반환될때 까지 대기 후 반환되면 다음 로직 동작 또는 종료
fun main(){
runBlocking {
val firstDeff = async {getFirst() }
val secondDeff = async {getSecond() }
println("processing...")
delay(500L)
println("waiting...")
val firstVal = firstDeff.await()
val secondVal = secondDeff.await()
println("result: ${firstVal + secondVal}")
}
}
suspend fun getFirst(): Int{
delay(1000L)
val value = Random.nextInt(100)
println("FirstVal: $value")
return value
}
suspend fun getSecond(): Int{
delay(1000L)
val value = Random.nextInt(1000)
println("SecondVal: $value")
return value
}
withContext
context의 전환을 쉽게 하기 위한 기능
화면에서 이미지를 요청, 요청된 이미지를 백그라운드에서 처리 후, 다시 기존 화면에 이미지를 보여주는 작업이 필요할 때
fun main(){
runBlocking {
launch(Dispatchers.Default) {
println("First context: $coroutineContext")
withContext(Dispatchers.IO){
println("Second context: $coroutineContext")
}
println("Third context: $coroutineContext")
}
}
}
Exception
coroutine 실행에 따라 예외처리 다름
launch
부모 자식 관계 상속을 통해서 예외처리
즉각적인 실패인 경우 예외처리
try-catch를 이용한 예외처리
async
결과처리가 완료될 때까지 예외 연기
만약 결과가 완료되지 않으면 예외는 처리되지 않음
coroutine 또는 await() 호출될 때 try-catch를 이용한 예외처리
fun main(){
runBlocking {
val myHandler = CoroutineExceptionHandler{ coroutineContext, throwable ->
println("Exception handler: ${throwable.localizedMessage}")
}
val job = GlobalScope.launch(myHandler) {
println("Throwing exception from job")
throw IndexOutOfBoundsException("exception!!")
}
job.join()
val deferred = GlobalScope.async {
println("Exception async")
throw ArithmeticException("exception!!")
}
try {
deferred.await()
}catch (e: ArithmeticException){
println("catch exception: ${e.localizedMessage}")
}
}
}
'학습 log (이론) > kotlin' 카테고리의 다른 글
Kotlin기초 'lambda' (0) | 2019.12.07 |
---|---|
Kotlin기초 'Closure' (0) | 2019.11.12 |
Kotlin기초 '유용한 함수' (0) | 2019.11.10 |
Kotlin기초 '함수형 프로그래밍' (0) | 2019.11.06 |
Kotlin기초 '객체' (0) | 2019.11.03 |