코루틴은 일시중단이 가능하다.
launch로 실행하든 async로 실행하든 내부에 해당 코루틴을 일시 중단 해야하는 동작이 있으면 코루틴은 일시 중단 된다.
# suspend
사전적 의미로는 '중지하다'라는 뜻이다.
coroutine에서는 시작하고, 멈추고, 다시 시작할 수 있는 함수라고 한다.
- suspend란 비동기 실행을 위한 중단 지점의 의미
- 즉, 잠시 중단(suspend)한다는 의미이고, 잠시 중단 후에 다시 시작(resume) 된다는 뜻
# Suspend function이 없다면?
하나의 thread가 block될 경우, 해당 thread는 다른 작업을 할 수 없는 block상태에 놓이게 된다.
즉, blocked 상태가 끝날 때까지 해당 thread는 중지 상태인 것.
하지만, suspend function을 사용한다면 blocked된 상태에 놓일 때, 그 작업을 supend하고 그 기간 동안 thread에서 다른 작업을 수행할 수가 있다. (자원 효율)
# 기본 순차 실행 (Sequential by Default)
기본적으로 Code & Function은 순차적(Sequential)으로 수행
일정시간 delay 후 Integer값을 반환하는 두개의 suspend(중단) 함수 생성
결과에서 총 수행시간을 보면 함수가 순차적으로 수행되었기 때문에 3016ms가 걸렸습니다.
먼저 testOne()이 1000ms대기 후 완료되고, 그 다음 testTwo()가 2000ms를 Delay하기 때문에 그렇습니다
# async 통한 동시 수행 (Concurrent using async)
위의 두 중단함수가 서로 인과관계(의존성)을 갖지 않는다면, 두 개의 함수를 동시에 수행시 더 빠른 동작을 구현 가능
launch{} 와 async{}는 동일한 컨셉이지만, return 객체의 차이가 있는 코루틴 빌더입니다.
launch{} -> Job return
async{} -> Deferred<T> return
Job은 launch로 생성된 코루틴의 상태를 관리하는 용도로 사용하고 결과값을 리턴받을 수 없으나,
Deferred는 async블럭 내 수행된 결과를 원하는 시점에 return받을 수 있다는 큰 차이가 있습니다.
Deferred는 job을 상속받아 구현되었기 때문에 Job의 기능을 사용할 수 있습니다.
두 개의 작업이 동시에 실행되었기 때문에 testTwo()의 delay(2000)을 마지막으로 모든 실행이 종료된 걸 확인 가능
# suspend function을 사용하는 경우와 하지 않을 때 thread 자원 사용 확인
- suspend 함수에서는 thread가 blocked되지 않고, suspend된 상태에서 다른 작업을 수행하는지?
- blocked된 작업이 suspend되고 다시 resume되는지?
- delay() 자체는 suspend 함수로 coroutineScope 에서만 사용 가능
- Thread.sleep() 은 suspend 함수가 아님.
1. suspend function 사용하지 않은 테스트 코드
2. suspend function 사용 테스트 코드
1. suspend function을 사용하지 않은 경우 결과
각 함수 Task1() 와 Task2() 는 각기 다른 thread 에서 수행되었다.
- Task1() : DefaultDispatcher-worker-2
- Task2() : DefaultDispatcher-worker-1
이유: Thread.sleep() 동안 thread가 blocked되어 다른 작업을 수행할 수 없기 때문에, 서로 다른 스레드에서 실행 된다.
2. suspend function을 사용한 경우
각 함수 Task1() 와 Task2() 는 하나의 동일한 thread(DefaultDispatcher-worker-1) 에서 수행되었다.
이유 : delay()동안 thread가 suspend되어 다른 함수의 작업을 수행할 수 있기 때문이다.
(suspend를 사용한다고 무조건 하나의 스레드에서만 실행되는 것은 아니다.)
여기서는 2개의 함수의 작업이 서로 다른 시간에 일어나기 때문에 가능!
# suspend function이 suspend된 후 resume할 수 있는 이유
- 함수에 suspend키워드를 추가하면 => 바이트코드를 보면 함수의 파라미터에 "Continuation"이 추가됨.
- 이 Continuation이 프로그램의 현재 상태를 저장한다. Continuation wrapper 내에 나머지 코드를 전달하는 것처럼 생각할 수 있다.
위의 suspendTask()함수의 디컴파일된 코드를 보면 함수 파라미터로 Continuation 객체가 들어가 있는 것을 확인할 수 있다.
1. Continuation 인터페이스
2. 실제 suspend함수가 Continuation파라미터가 추가되고 completion.resume에 값을 넣는 모습
3. 해당 소스에 Label로 상태가 저장/관리 되는듯(나중에 다시 suspend되었다가 resume시에 라벨로 실행하기 위해(?))
4. 라벨에 따른 소스 실행
5. 내부 동작은 실제 함수의 StateMachine이름의 CoroutineImpl(=Continuation)을 리턴하는 클래스가 만들어지는 듯(?)
라벨0 : user객체
라벨1 : userDb객체
라벨2 : result객체 리턴
6. StateMachine에 의해 continuation을 가져오고 해당 라벨 실행
# Continuation는 누가 전달해 줄까?
보통 CoroutineScope의 launch 함수 내에서 suspend를 호출하게 됩니다.
StandaloneCoroutine class는 아래와 같이 구현되어 있습니다.
이 중 AbstractCoroutine<> 클래스 정보가 보입니다.
AbstractCoroutine 클래스는 아래와 같습니다.
여기에 보면 Continuation<>을 상속받고 있음을 확인할 수 있습니다.
결국 launch 내에서 suspend 함수를 부르면 AbstractCoroutine에서 상속받고 있는 Continuation이 있기에 이를 활용해 suspend 함수가 구현되어 있음을 알 수 있습니다.
Continuation는 interface로 정의되어 있는데, resumeWith() 함수를 포함하고 있습니다.
resumeWith 함수를 통해 suspend 함수의 작업이 완료되었을 경우 resumeWith()으로 복귀시키게 됩니다.
# 코루틴 suspend function 추가 종류(?)
withTimeout : 코루틴이 정해진 시간 안에 실행되지 않으면 예외를 발생시킨다.
withTimeoutOrNull : 정해진 시간 안에 실행되지 않으면 null을 반환한다.
awaitAll : 모든 작업의 성공을 기다리면서 작업중에 하나라도 실패하면 awaitAll 또한 실패한다.
joinAll : 모든 작업이 끝날 때 까지 현재 작업을 일시 중단시킨다.
결론
- coroutine suspend 함수는 thread 를 block 하지 않는다.
- 따라서, 하나의 thread 에서 여러 개의 coroutine 을 실행할 수 있다.
특정 작업이 suspend 되고 resume 될 때까지 이 사이에 다른 작업을 수행할 수 있기 때문. - 그래서 suspend 는 많은 concurrent 작업을 지원하면서 blocking 에 대한 메모리를 절약할 수 있다.
참조 :
https://nuritech.tistory.com/16#suspend_%EB%8A%94_%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80._
https://jaejong.tistory.com/63
https://thdev.tech/coroutines/2021/07/22/Coroutine-suspend/
https://velog.io/@rhkswls98/Android-Kotlin-Coroutine-%EC%A0%95%EB%A6%AC
'안드로이드 > Android 비동기 처리' 카테고리의 다른 글
[Android] Coroutine StateFlow, Flow, LiveData, ViewModelScope, LifecycleScope + Paging3 (1) | 2024.03.20 |
---|---|
비동기 처리(process&thread) (0) | 2022.06.07 |
CoroutineContext (0) | 2022.04.16 |
코루틴 Coroutine (0) | 2022.04.16 |
비동기 처리 - 블로킹과 논블로킹 (0) | 2022.04.13 |