Coroutine
Updated:
Coroutine - kotlin
- 개념 : 코루틴(coroutine)은 루틴의 일종으로서, 협동 루틴이라 할 수 있다(코루틴의 “Co”는 with 또는 togather를 뜻한다). 상호 연계 프로그램을 일컫는다고도 표현가능하다. 루틴과 서브 루틴은 서로 비대칭적인 관계이지만, 코루틴들은 완전히 대칭적인, 즉 서로가 서로를 호출하는 관계이다. 코루틴들에서는 무엇이 무엇의 서브루틴인지를 구분하는 것이 불가능하다.(위키피디아)
-
서로 협력해서 실행을 주고 받으면서 작동하는 여러 서브루틴을 말한다.
- 코루틴은 서브루틴의 확장개념이라고 볼 수 있다.
- 함수와 다르게 시작과 끝이 아닌 로직의 어느 부분에서도 시작과 종료가 이루어 질 수 있다.
Coroutine 과 Subroutine 의 비교 - 이미지출처
-
VS Threads - 차이점 참조
-
Thread
OS의 Native Thread에 직접 링크되어 동작하여 많은 시스템 자원 사용
Thread간 전환 시에도 CPU 상태 체크가 필요하므로 그만큼의 비용 발생
마음대로 stop시 죄악으로 취급받을 수 있다.
-
Couroutine
즉시 실행이 아니고, OS의 영향을 받지 않아 cost가 많이 들지 않음
전환 시 Context Switch 가 일어나지 않음
루틴 실행, 종료 여부를 직접 지정 가능
-> 작업 전환 시 시스템의 영향을 받지 않아 그에 따른 비용 발생 X(semaphores, mutexes 등 필요 X)
동시성은 제공하지만 병렬은 제공하지 않음
-
-
kotlin
-
CoroutineContext & CoroutineScope - 참조
-
코루틴을 어떻게 처리할 것인지에 대한 여러가지 Element를 포함 -> Element의 집합
Interface로, 코루틴에 대한 설정 요소를 등록하고, Scope의 속성이된다.
-
4가지 메소드 존재 - <get(), folde(), plus(), minusKey()>
3가지 종류의 구현체로 존재
-
EmptyCoroutineContext - Default, Singleton 객체 사용
-
CombinedContext : 2개 이상의 Context가 명시되면 Context간 연결을 위한 컨테이너 역할을 하는 Context
-
Element : Context의 각 Element들도 CoroutineContext를 구현함
-
-
CoroutineContext 하나만 멤버속성으로 정의하고 있는 인터페이스
public interface CoroutineScope { /** * Context of this scope. */ public val coroutineContext: CoroutineContext }
코루틴 빌더들은 CoroutineScope의 확장함수로 CoroutineScope의 함수로 호출됨
코루틴 빌더를 통해 생성되는 코루틴들은 CoroutineScope의 멤버속성에 기반으로 생성됨
명시해 주지 않으면 default인 EmptyContext로 할당
- GlobalScope랑 같다는 의미 이며 Activity가 아닌, Application의 생명주기에 종속된다는 의미
- 따라서 Activity에 종속적으로 사용하려면 Activity에 Scope를 초기화 설정 해줘야함. - 참조
class MainActivity : AppCompatActivity(), CoroutineScope { // CoroutineScope 인터페이스 구현 lateinit var job :Job override val coroutineContext :CoroutineContext get() = Dispatchers.Main + job // Activity 생성 시 job 할당 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) job = Job() } // Activity 소멸 시 job 소멸 override fun onDestroy() { super.onDestroy() job.cancel() // Cancel job on activity destroy. After destroy all children jobs will be cancelled automatically } }
-
-
-
- CoroutineContext의 주요요소
- CoroutineContext을 상속 받아 어떤 스레드를 이용하여 어떻게 동작할 것인지 미리정의
Dispatchers.Default : CPU 사용량이 많은 작업에 사용. Main Thread 에서 작업하기에는 너무 긴 작업들에게 알맞음 Dispatchers.IO : 네트워크, 디스크 사용시 사용. File을 읽고, 쓰고, Socket을 읽고, 쓰고 작업을 멈추는 것에 최적화 되어있음 Dispatchers.Main : Android 의 경우 UI 스레드를 사용
이외에 코루틴 공식 문서에 Dispatchers.Unconfined도 존재
- 다른 Dispatcher 와 달리 특정 스레드(or 풀) 지정하지 않음
-
코루틴 사용 순서
- 사용할 Dispatcher 결정
- Dispatcher 이용하여 CoroutineScope 생성
- CoroutineScope 의 launch 또는 async에 수행할 코드 블록을 넘기면 됨
- Basick coroutine code
CoroutineScope(Dispatchers.Main).launch{ //foreground task } CoroutineScope(Dispatchers.Main).launch(Dispatchers.Default){ //CoroutineContext change so working task by converting to Background }
- Background task
val scope = CoroutineScope(Dispatchers.Main) CoroutineScope(Dispatchers.Default).launch{ //Background task by new CoroutineScope } scope.launch(Dispatchers.Default){ //CoroutineScope는 유지되면서 작업이 처리되는 스레드만 변경됨 }
- 제어 범위가 다를 시 발생하는 현상
- 멈추지 않는 내부 coroutine block
val scope = CoroutineScope(Dispatchers.Main) val job = scope.launch{ //... CoroutineScope(Dispatchers.Main).launch{ //scope coroutine이 취소 되어도 지속 수행 } //... } job.cancel()
-
Coroutine 제어를 위한 주요 keyword - 참고
-
launch() - Job
- Coroutine block은 job 객체를 반환
val job : Job = launch{ //... }
- launch() 함수로 정의된 코루틴 블록은 즉시 수행되며, 반환 받은 Job객체는 해당 블록을 제어 할 수 있지만, 결과를 반환하진 않음 - 결과 반환은
async()
이용
-
async() - Deferred
- Deferred 객체를 반환
val deferred : Deferred<T> = async{ //... T //Result }
- Deferred 객체를 이용해 제어가 가능하고, 동시에 결과값을 반환받을 수 있음
-
지연실행 - start 인자에 CoroutineStart.LAZY 를 사용하면 해당 코루틴 블록은 지연 실행
val job = launch(start = CoroutineStart.LAZY){ // ... } or val deferred = async(start = CoroutineStrat.LAZY){ // ... }
-
runBlocking()
- 코드 블록이 작업을 완료하기를 기다림
runBlocking{ //... }
- runBlocking() 함수로 시작된 블록은 아무런 추가함수 호출 없이 해당 블록이 완료될때까지 기다릴 수 있음
- runBlocking 이 사용하는 스레드는 현재 runBlocking() 함수가 호출된 스레드
- Android 의 경우 runBlocking() 함수를 메인 스레드 에서 호출하여 시간이 오래 걸리는 작업을 수행하는 경우 ANR이 발생할 위험이 있으므로 주의해야함
-
-
CoroutineScope vs GlobalScope - 참조