1. 코루틴이란
"경량 쓰레드?" "루틴?" "코틀린 코루틴?"
"비동기 처리를 하기 위한 하나의 방법론, 그 방법은 중단, 재개임"
다양한 언어에서 Coroutine을 지원함"Kotlin, GoRoutine, C++20~.."
---------------------------------------------------------------------------------------------------
2. 페이지
코루틴을 이해하기 하기 위한 그림을 하나 보겠음
"(일반적인 길(fun)) 화살표 표시"
일반적인 함수는 루틴이라고 하여 호출과 return까지의 길은 하나임
(일반적인 길(표지 suspend)) (중간에 구멍) -> (표기) -> 다른터널
코루틴은 이런 루틴한 길을 가다가 잠시 멈춰야 하면 출구가 나오기 전에 나와서 기록하고
다른 루틴을 돌러간다.
(나중에 다시 돌아와) -> 들어가서 -> 출구로 나옴
---------------------------------------------------------------------------------------------------------
3. 코루틴이 재개를 할 수 있는 원리
나올때의 상태를 표기하는걸 Continuation 객체를 통해 함
{예제 코드 필요}
suspend가 동작하면 Continuation에 현재 상태를 Int로 저장하고 이때까지 생성한 객제를 Continuation을 통해 기입함
resume이 되어 COntinuation을 불러 중지된 상태에서 재시작함
코루틴 런타임은 이 메모된 객체를 가지고 어느 컨텍스트에서 어떤 Dispatcher로 관리되는지 파악하고 재개해주는 역할을함
우리는 해당 Suspend로 적혀있는 곳을 갈때는 이미 이게 중간에 정지하는 위치가 있으니 메모장을 가지고 들어가야합니다.
{coroutineScope.launch{} 의 예제 코드를 보여주며}
메모장이 비치되어있는 Coroutine 빌더를 사용해 메모장을 준비해야 suspend fun을 출입할 수 있음.
------------------------------------------------------------------------------------------------------------------------------------
4. 왜 경량 쓰레드인가
Java의 Thread나 OS에서 관리하는 일반 Thread가 아님
Thread라는 말이 붙어있어서 헷갈릴 수 있지만
작업의 단위로서의 Thread가 좀 더 맞는 말임
Thread는 이 그림처럼 Process, 안드로이드 앱이라 치면 해당 앱 밖의 OS에 의해서 메모리를 할당받고 별도의 공간을 받음
하지만 코루틴은 그저 사용자 앱수준에서 정의한 작업임
앱 내의 코루틴 스캐쥴러에 의해서 관리됨
"경량"이라는 건
Thread는 Thread끼리 교체할 때 Context Switching을 통해 상태를 저장하고 불러오며 큰 리소스가 소요됨
Coroutine도 Coroutine끼리 교체될 때 Context Switching을 진행하지만 이는 앱 수준에서 스캐쥴러와 디스패쳐 그리고
프로세스 힙 영역에 Coroutine의 상태를 저장하고 있는 Continuation에 의해서 상태를 저장하고 불러드림
OS에 의해서 전환이 전환이 일어나는 것이 아닌 앱내에서 그저 객체를 참조하는 수준으로 ContextSwitching이 발생함
------------------------------------------------------------------------------------------------------------------------------------------------------------
5. 헷갈렷던 부분
"경량 쓰레드"인 코루틴은 Context Switching시 가볍게 이루어 진다.
위 글은 나에게 너무 큰 오해를 불러일으켰음.
Coroutine도 결국 IO나 Default냐에 따라 정해져 있는 Thread Pool이 존재하고 해당 작업을 그 ThreadPool에서 진행한다는 것임
그럼 메인 쓰레드에서 사진을 백그라운드 쓰레드에서 동작하는것과 무슨 차이가 있냐 라는거임
위 처럼 메모리(CPU)에는 어쨋든 하나의 쓰레드만 올라갈 것이고 코루틴도 쓰레드를 왔다갔다하니 붉은 지점에 Context Switching이 발생하게 되어있음
Thread를 하나 더 만들어서 동작시키는 것과 별반 다른게 없어보임
맞음
차이가 없음
그럼 그냥 직관적인 코드나 개발의 편의성을 위해서만 코루틴을 쓰냐?
그건 또 아님
----------------------------------------------------------------------------------------------------------------------------------------------------------
6. 프로세스가 차지할 수 있는 크기는 정해져 있음
그런데 프로세스 내에 비동기 처리를 할때 마다 쓰레드를 만든다고 할때 쓰레드는 지정된 크기를 할당받기 때문에 프로세스가 점점 비좁아 짐
수용할 수 있는 만큼 쓰레드를 만들어서 비동기처리를 하게되면 더이상 쓰레드를 만들 수 없을 때가 올거임
자 이제 더이상 다른 쓰레드가 끝나기 전까지는 비동기 처리를 할 수 없음
물론 이런 상태를 발생시키지 않도록 ThreadPool을 지정하고 Excute해서 성능을 떨어지겠지만 안전하게 동작시킬 수도있는데... 그런 코루틴의 장점을 설명하고자 한 설정이니 그냥 봐주셈
자 근데 코루틴은 뭐였냐면, 그냥 함수와 컨티뉴에이션 객체의 집합임 이 두개가 합쳐지면서 SUSPENDABLE FUN이 된거였음
그럼 어느 쓰레드에서 동작을 시키던 프로세스 힙 영역에 존재하는 컨티뉴어스 객체와 함수만 참조해서 실행 시키면됨
그리고 Dispatcher에 코루틴으로 사용할 수 있는 IO, Default 쓰레드도 지정된 갯수로 미리 할당되어 해당 Thread를 사용해서 쉬고있는 Thread에 스캐쥴러가 실행시킬 Coroutine을 동작시키기만 하는것임
그니까 자갈(쓰레드)로 바구니를 채우느냐 모래알갱이로 바구니를 채우느냐임 {그림 필요}
이러면 한정된 쓰레드 내에서 수많은 비동기 처리를 중지-재개하면서 처리할 수 있음.
코루틴의 가장 큰 장점은 해당 Thread에 여러 코루틴이 동작하게되면서 Thread ContextSwitching이 최소화 되도록 조절하여 동작시키는 것임
회사 코드로 로그 보여주기
--------------------------------------------------------------------------------------------------------------------------------------------------------------
7. 그러면 콜백으로 하는건 왜 안되냐 차이가 뭐냐 할 수 있는데...
둘 다 중지 - 재개를 통해 진행하지만
콜백은 작업이 끝난 후 호출될 함수를 이벤트 큐에 예약하고 필요시 순차적으로 실행하므로 추가 쓰레드가 필요하지 않치만
UI 랜더러 작업이 많이 필요한 앱에
메인 쓰레드를 이런 콜백작업들로 잡아먹게되면 문제가 있으므로
백그라운드 쓰레드를 사용한다는 가정을 한다면
1. ContextSwitching을 최소화 최적화 할 수 있고
2. 가독성이 훨신 뛰어난
ex{콜백 지옥과 코루틴 차이를 보여주는 코틀린 코드}
fun fetchData(callback: (String) -> Unit) {
apiCall1 { result1 ->
apiCall2(result1) { result2 ->
apiCall3(result2) { result3 ->
callback(result3) // 중첩된 콜백 지옥
}
}
}
}
suspend fun fetchData(): String {
val result1 = apiCall1()
val result2 = apiCall2(result1)
val result3 = apiCall3(result2)
return result3
}
fun main() = runBlocking {
// 코루틴에서 비동기 작업을 수행
val result = fetchData()
println("Final Result: $result")
}
코틀린이 더 효율적인 방법이라고 생각함
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
8. 실제 코드 동작 비교API 테스트
1) 코루틴을 사용한 방식 1, 10, 50, 500
2) Threading 1, 10, 50, 500
아마도 시간적인 차이는 나지않겠지만 보여주기만 할듯?
//////////////////////
코루틴의 장점
- 경량성: 코루틴은 경량 스레드로 불리며, 실제로 새로운 스레드를 생성하지 않고도 여러 비동기 작업을 수행할 수 있습니다. 이로 인해 Thread Pool에서 스레드를 관리하는 오버헤드가 줄어들고 메모리 및 리소스 사용이 효율적입니다.
- 컨텍스트 스위칭 최소화: 코루틴은 스레드 풀에서 여러 코루틴을 관리하며, 필요한 시점에만 스레드 간 전환이 이루어지도록 합니다. 여러 코루틴이 한 스레드에서 실행되므로, 스레드 컨텍스트 스위칭이 최소화됩니다. 예를 들어, Dispatchers.IO는 제한된 수의 스레드에 많은 I/O 작업을 할당하여 스레드 풀 내에서 효율적으로 사용됩니다.
- 중단 및 재개: 코루틴의 suspend 키워드를 통해 함수 실행을 **중단(suspend)**하고 필요한 시점에 **재개(resume)**할 수 있습니다. 이를 통해 UI를 블로킹하지 않고도 백그라운드에서 작업을 처리하고 결과를 받아 UI에 업데이트할 수 있습니다.
- 가독성과 유지보수성: 콜백을 사용한 비동기 처리는 종종 코드가 복잡해지고 읽기 어려운 "콜백 지옥" 문제를 발생시킵니다. 코루틴은 비동기 흐름을 직관적으로 표현할 수 있어 가독성이 뛰어나며 유지보수하기 쉽습니다.
----------------------------------------------------------------------------------------------------------------------------------------------------------
1. 선정배경
----------------------------------------------------------------------------------------------------------------
2.OJT 보고
일정 추가 + 서치 화면 표출 추가
----------------------------------------------------------------------------------------------------------------------------------------------------------
3. 문제제기
gRPC통신 등 비동기 처리를 코루
----------------------------------------------------------------------------------------------------------------------------------------------------------
9. 해결과정
코루틴은 경량 쓰레드
Thread
Context Switching 시 TCB에 현 상태 저장
OS나 JVM Thread Sceduler에 의해 생성, 교체 등 관리
----------------------------------------------------------------------------------------------------------------------------------------------------------
10.
Coroutine
Context Switching 시
Kotlin의 Continuation 객체에 현 상태 저장(Heap)
Process내 사용자 수준에서 관리 : Coroutine Scheduler
----------------------------------------------------------------------------------------------------------------------------------------------------------
생성된 작업 단위인 Coroutine은 Coroutine Scheduler와 Dispatcher에 의해서 최적화 하여 자동 분배
----------------------------------------------------------------------------------------------------------------------------------------------------------
Context Switching 비용이 적다
----------------------------------------------------------------------------------------------------------------------------------------------------------
그림만 있는 페이지
최적의 시나리오는 무작정 분배해서 여러 Thread에서 동작하는게 아님
겹치지 않고 Threa Switching이 적절히 이루어 지도록 분해
Scheduler가 동작
----------------------------------------------------------
결론
1. View, Data Layer가 분리된 구조에서 독립적인 구조를 유지하기 위해 Observe 객체 사용하는건 유리함
Coroutine을 통해 비동기 처리 및 풍부한 라이브러리 사용해서 앱 최적화
2. Thread를 통한 비동기 처리는 MMO등 메모리 관리에 민감함
- ThreadPool 및 Excutor사용해도 되지만 Manager로 최적화 시켜주는 Coroutine 적합
3. 콜백 사용 시 MMO문제는 해결될 지 몰라도 UIThread 최적화 및 코드 가독성을 위해 Coroutine 사용
4. 코루틴은 방법론이고 다양한언어에서 다양한 방식으로 제공함, 코틀린 언어 및 코틀린X라이브러리에서 편리하게 사용할 수 있으므로 적극 활용하는게 좋을
- 다양한 방식이 있지만
--------------------------------------------------------------------------------------------------------------------
'어플 개발일기' 카테고리의 다른 글
MutableStateFlow(Kotlin) -> CurrentValueSubject(swift) (0) | 2025.02.08 |
---|---|
24.7.31. 네트워크 중복 호출과 Flow오류 수정 (0) | 2024.07.31 |
24.7.29. AGP 8.5.1 업데이트와 환경설정(SDK, JVM 등) (0) | 2024.07.31 |
24.7.28-29. 앱 내에서 게시글 동기화 문제 해결 (0) | 2024.07.29 |
안드로이드 개발과 관련해서 알아가기(1) (0) | 2024.07.09 |