반응형
운영중인 앱에는 문자를 전송하는 기능이 있다. 문자를 전송하는 로직은 chunked()를 통해 배치를 만들고, 배치 내부는 비동기로 실행하되, 배치 내부의 작업이 모두 끝나면 다음 배치로 넘어가는 순차 방식으로 구현이 되어 있었다.
// mmsList: List<MmsReq>
val result = mmsList.chunked(10).flatMap { batch ->
batch.map { job ->
async { sendMms(job) }
}.awaitAll()
}
위 방식은 단건씩 처리하는 것과 비교했을 때 효율을 높이고, 처리량을 조절함으로써 로컬과 서버에 부담을 주지 않는다는 장점이 있다. 하지만 현재 배치의 작업이 지연이 생겼을 때 다음 배치의 시작이 지연되어 처리 속도가 떨어질 수 있다.
코루틴에서 제공하는 semaphore()는 동시에 permit을 획득할 수 있는 작업 수를 제한하여, 하나의 작업이 끝나면 다음 작업을 자동으로 채워서 동시 실행 수를 유지시켜준다.
// mmsList: List<MmsReq>
val semaphore = Semaphore(10)
val result = mmsList.map { mmsData ->
async {
semaphore.withPermit {
sendMms(mmsData)
}
}
}.awaitAll()
semaphore() 를 적용하면서 배치에 대한 신경을 쓰지 않아도 되서 좋았고, 코드의 일부분을 정리해서 올렸지만 chunked()를 썼을 때와 달리 코드가 정돈됨을 느꼈다.
단, semaphore를 사용하면 현재 구조상 아이템의 개수만큼 코루틴을 생성하기 때문에 작업 데이터가 굉장히 많은 경우 메모리를 크게 잡아 먹게 된다. 그리고 withPermit이 실행되면서 코루틴이 일시 중단되었다가 재개되는데, 아이템의 개수가 많고 개별 작업 시간이 매우 짧다면 스위칭으로 인한 오버헤드가 작업 시간보다 커지는 문제가 발생할 수 있다고 한다. (작업 개수만틈 Deferred 객체가 생성 됨)
두 함수를 비교하여 사용할 때 충분한 테스트가 필요할 것 같다.
chunked
- 작업시간에 편차가 없는 경우
- 리스트가 매우 큰 경우
- 코루틴 개수와 메모리를 확실히 제한하려는 경우
semaphore
- 작업시간에 편차가 있는 경우
- 리스트가 고정되지 않고 추가 및 삭제로 변경되는 경우 동시성을 제한할 때
- 전체 작업 시간을 단축하고 싶을 때(특히 작업마다의 처리 시간이 들쭉날쭉할 때)
반응형
'작업 일지' 카테고리의 다른 글
| [Android] Worker Pool 패턴을 통한 작업의 효율/안정성 올리기 (1) | 2026.01.20 |
|---|---|
| [Android] ConnectivityManager로 데이터 절약 모드 감지하기 (0) | 2026.01.06 |
| [Android] WebView에서 다운로드 동작이 안 되는 이유 (0) | 2025.09.17 |
| [Android] 배송 순서 최적화하기 with TSP (2) | 2025.07.01 |
| [Android][Compose] MaterialCalendarView를 Compose에서 사용하기 (0) | 2024.05.19 |