일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
- Android
- viewModelScope
- NestedScrollView
- 절대 주소
- DiffUtil
- 운영체제
- 물리 메모리
- Kotlin
- flutter
- 자이고트
- GetX
- appcompatactivity
- appcompatacitivity
- 내부 단편화
- 상태관리
- apk 빌드 과정
- Dispatchers
- 뷰홀더
- AAC
- AsyncListDiffer
- http발전과정
- 리사이클러뷰
- 안드로이드
- 디자인 패턴
- recyclerview
- 플로이드워셜
- 리사이클러뷰풀
- 프로세스
- 데코레이터 패턴
- http 역사
- Today
- Total
hong's android
[안드로이드] 코루틴의 Suspend와 CPS (수정중) 본문
코루틴
- 코루틴은 스레드 내부에 실행되는 경량 스레드입니다.
- Main - safe 한 동시성을 지원하고 콜백보다 가독성이 좋습니다.
- suspend 함수를 통해서 일시중단 - 재개할 수 있는 여러 진입 지점을 허용합니다.
- 이를통해 서브루틴들이 협력형으로 번갈아 가며 실행될 수 있다.
- 협력형으로 동작하기 위해 CPS 패러다임을 사용합니다.
CPS(Continuation Passing Style) 패러다임
기본적인 원리는 매개변수로 콜백 인터페이스를 가진 Continuation 객체를 전달합니다.
매개변수를 받은 메소드에선 이를통해 다시 suspend된 메소드를 실행합니다.
결국 매개변수로 Continuation 객체를 전달하며 현재 supend된 시점에서 resume되게 만들어 같은 스레드 내부에서 협력형으로 동작 할 수 있습니다.
/**
* Interface representing a continuation after a suspension point that returns a value of type `T`.
*/
@SinceKotlin("1.3")
public interface Continuation<in T> {
/**
* The context of the coroutine that corresponds to this continuation.
*/
public val context: CoroutineContext
/**
* Resumes the execution of the corresponding coroutine passing a successful or failed [result] as the
* return value of the last suspension point.
*/
public fun resumeWith(result: Result<T>)
}
먼저 Continuation 인터페이스를 살펴보면 결과값을 받는 resumeWith() 재개함수, 해당 코루틴을 실행하는 context를 담는 CoroutineContext를 가집니다.
CPS 패러다임은 어떤식으로 동작하는지 확인하기 위해 다음과 같은 로직을 만들었습니다.
두개의 suspend 함수 getUserId(), getUserNickname() 를 같은 스레드에서 비동기로 실행했을때
서로 supend, resume 번갈아가며 실행 합니다.
runBlocking {
val one = async { getUserId() }
val two = async { getUserNickname() }
println("결과값 : ${one.await()} + ${two.await()} ")
}
suspend fun getUserId() : String{
delay(1000L)
return "myid1"
}
suspend fun getUserNickname() :String{
delay(500L)
return "hong"
}
suspend 함수 getUserNickname() 의 동작원리를 파악하기위해 디컴파일 해보겠습니다.
결과는 아래와 같습니다.
@Nullable
public final Object getUserNickname(@NotNull Continuation var1) { #1
Object $continuation;
label20: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var1) { #2
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return MainActivity.this.getUserNickname(this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(((<undefinedtype>)$continuation).label) { #3
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1; #4
if (DelayKt.delay(500L, (Continuation)$continuation) == var4) {
return var4;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return "hong";
}
변경된 부분을 확인 해보면
1. 코틀린 컴파일러에 의해 suspend 키워드가 제거되고 getUserNickname() 함수의 매개변수에 Continuation 구현 객체가 추가된 것을 볼 수 있습니다.
2. 메소드 안에 Continuation을 구현한 ContinuationImpl 클래스의 객체를 생성합니다. 해당 Continuation 객체 내부엔 결과를 저장하는 result, 중단 지점을 표시할 label이 존재합니다. 또한 자기 자신을 실행 시킬 수 있는 invokeSuspend() 함수가 존재합니다.
3. 실행시키고자 하는 함수에
4. 해당 label을 실행시키면 Continuation객체의 label을 증가시킵니다.
-> 내부에 다른 함수가 있단면 Continuation객체를 전달한다.
Reference.
1. https://myungpyo.medium.com/코루틴-공식-가이드-자세히-읽기-part-1-dive-3-b174 c735 d4 fa
2. https://jisungbin.medium.com/코루틴의-cps-구현-살펴보기-7 b9 cd5 f5 c7 bd
'Android > Coroutine' 카테고리의 다른 글
[안드로이드] 코루틴 CoroutineScope와 Dispatchers (0) | 2023.03.21 |
---|---|
[안드로이드] Coroutine 이란? (1) | 2023.02.28 |