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

[Kotlin] Coroutines tutorial - async code 쉽게 짜기

by 돼지왕왕돼지 2018. 11. 25.

[Kotlin] Coroutines tutorial - async code 쉽게 짜기


https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md


\actor, Async, async code 쉽게 짜기, await, Bridging blocking and non-blocking worlds, buffered channel, cancel and join, cancellable, cancellationexception, CommonPool, CompletableDeferred, Composing suspending functions, consumeEach, coroutine builder, coroutine channels, Coroutine dispatcher, coroutine example, coroutine hello world, coroutine parent assign, coroutine tutorial, CoroutineContext, CoroutineName, Coroutines are like daemon threads, CoroutineStart.LAZY, default dispatcher, DefaultDispatcher, deferred return, deffered, delay, Extract function refactoring, ForkJoinPool, hello world coroutine, isActive, Job, job cancel, job timeout, Join, jvm option, kotlin coroutine, kotlinx.coroutines.debug, launch, launch return, launch vs thread, Lazy, light-weight thread, mutex, newSingleThreadContext, NonCancellable, onAwait, Parent, pipeline pattern, produce, producer-consumer pattern, ReentrantLock, runBlocking, Select expression, Shared mutable state and concurrency, sleep, sleep vs delay, start param, suspend function, suspending function, suspending receiver, suspending send, Synchronization, synchronized, thread, Thread confinement, thread pool, thread 지정, TimeoutCancellationException, unbeffered channel, Unconfined, Waiting for a job, withContext, withTimeout, withTimeoutOrNull, yield, [Kotlin] Coroutines tutorial - async code 쉽게 짜기, 미지정, 코루틴, 코루틴 hello world


-

Kotlin coroutines 는 async code 를 sync code 의 구조를 바꾸지 않고 짤 수 있게 도와준다.




Coroutine Basics - modified

내용이 바뀌어서 새로 정리하였습니다. -> Coroutine Basics ( 코루틴 기초 )



예전 기록을 보고 싶으시면 아래 "더보기" 버튼을 눌러주세요!





Cancellation and timeouts - modified

내용이 바뀌어서 새로 정리하였습니다. -> Cancellation and Timeouts ( 취소와 타임아웃 )




예전 기록을 보고 싶으시면 아래 "더보기" 버튼을 눌러주세요!




Composing suspending functions - modified

내용이 바뀌어서 새로 정리하였습니다. -> Composing Suspending Functions ( suspending 함수 만들기 )





예전 기록을 보고 싶으시면 아래 "더보기" 버튼을 눌러주세요!




Coroutine context and dispatchers - modified

내용이 바뀌어서 새로 정리하였습니다. -> [Coroutine] Coroutine context and dispatchers



예전 기록을 보고 싶으시면 아래 "더보기" 버튼을 눌러주세요!





Asynchronous Flow - new

따로 정리하였습니다 -> [coroutine] Asyncronous Flow






Exception handling and supervision - new

따로 정리하였습니다. -> [coroutine] Exception handling and supervision 





Channels - modified ( now not experimental! )

따로 정리하였습니다. -> [coroutine] Channels




예전 기록을 보고 싶으시면 아래 "더보기" 버튼을 눌러주세요!







Shared mutable state and concurrency - modified

따로 정리하였습니다. -> [coroutine] Shared mutable state and concurrency





예전 기록을 보고 싶으시면 아래 "더보기" 버튼을 눌러주세요!






Select expression - experimental (core 1.0.1 기준)


-

Select expression 은 여러개의 suspending function 들을 await 하고 그 중 먼저 끝나는 녀석을 선택할 수 있는 기능을 제공한다.



-

fun fizz(context:CoroutineContext) = produce<String>(context){

    while(true){

        delay(300)

        send(“Fizz”)

    }

}


fun buzz(context:CoroutineContext) = produce<String>(context){

    while(true){

        delay(500)

        send(“Buzz!”)

    }

}


suspend fun selectFizzBuzz(fizz:ReceiveChannel<String>, buzz:ReceiveChannel<String>){

    select<Unit>{

        fizz.onReceive{ value ->

            println(“fizz -> ‘$value’”)

        }

        buzz.onReceive{ value ->

            println(“buzz -> ‘$value’”)

        }

    }

}


val fizz = fizz(coroutineContext)

val buzz = buzz(coroutineContext)

repeat(7){

    selectFizzBuzz(fizz, buzz)

}

coroutineContext.cancelChildren()


/* 결과는..

fizz -> 'Fizz'

buzz -> 'Buzz!'

fizz -> 'Fizz'

fizz -> 'Fizz'

buzz -> 'Buzz!'

fizz -> 'Fizz'

buzz -> 'Buzz!’

*/



-

onReceive 는 channel 이 close 되거나 안쪽로직에서 Exception 이 나거나 하면 fail 한다.

그래서 이 대신 onReceiveOrNull 을 사용할 수 있다.

이 때는 channel 이 close 되면 value 로 null 값이 들어온다.



-

Select expression 은 onSend clause 를 가지고 있다.

이 녀석은 selection 의 biased 특성과 잘 어울린다.

fun produceNumbers(context:CoroutineContext, side:SendChannel<Int>) = produce<Int>(context){

    for(num in 1..10){

        delay(100)

        select<Unit>{

            onSend(num){ } // send to primary channel

            side.onSend(num){ } // send to side channel

        }

    }

}


val side = Channel<Int>()

launch(coroutineContext){

    side.consumeEach{ println(“Side channel has $it”) }

}

produceNumbers(coroutineContext, side).consumeEach{

    println(“Consuming $it”)

    delay(250)

}

println(“Done consuming”)

coroutineContext.cancelChildren()


/* 결과는..

Consuming 1

Side channel has 2

Side channel has 3

Consuming 4

Side channel has 5

Side channel has 6

Consuming 7

Side channel has 8

Side channel has 9

Consuming 10

Done consuming

*/



-

deferred value 는 onAwait clause 를 통해 선택할 수 있다.

fun asyncString(time:Int) = async{

    delay(time.toLong())

    “Waited for %time ms"

}


fun asyncStringsList():List<Deferred<String>>{

    val random = Random(3)

    return List(12){ asyncString(random.nextInt(1000)) }

}


val list = asyncStringList()

val result = select<String>{

    list.withIndex().forEach{ (index, deferred) ->

        deferred.onAwait { answer -> 

            “Deferred $index produced answer ‘$answer’”

        }

    }

}

println(result)

val countActive = list.count { it.isActive }

println(“$countActive coroutines are still active”)


/* 결과는.. ( 4, 128, 11 값 모두 상황에 따라 다를 수 있을 것 같다. )

Deferred 4 produced answer 'Waited for 128 ms'

11 coroutines are still active

*/




댓글17