앱에서 비동기 작업을 처리할 때는 중복 실행으로 인한 불필요한 리소스 사용이나,
예상치 못한 오류로 인한 비정상 종료 등의 문제가 발생할 수 있습니다.
이를 방지하기 위해 중복 실행으로 인한 문제여부를 체크하고 중복 실행에 대한 작업을 추가하는 것은
앱의 안정성을 높일 수 있는 방법 중 하나입니다.
따라서, 스레드 및 코루틴을 이용한 비동기 작업을 처리할 때 중복 실행을 관리하는 방법에 대해 정리해보려고 합니다.
스레드(Thread) 중복 실행 방지 : Flag
"isThreadRunning"이라는 플래그를 사용해서 연속적으로 호출되더라도 중복해서 실행되는 것을 막을 수 있습니다.
private var isThreadRunning = false
private fun threadExample() {
if (isThreadRunning) { // 중복 실행 방지
println("Thread is already running.")
return
}
val thread = Thread {
isThreadRunning = true
var count = 0
while (count <= 5) {
println("currentThread(${Thread.currentThread().name}), count $count")
count++
Thread.sleep(1000)
}
isThreadRunning = false
}
thread.start()
}
비동기 작업에서 플래그 사용 시 주의사항!!!
비동기 작업 특성상 여러 스레드에서 서로 다른 시간에 실행되기 때문에
플래그를 올바르게 관리하지 않으면 예상치 못한 결과가 발생할 수 있습니다.
(ex. 여러 스레드에서 동시에 플래그에 접근하거나 변경하는 경우)
스레드(Thread) 중복 실행 방지 : Executors.newSingleThreadExecutor
Executors.newSingleThreadExecutor를 사용하면 중복 실행을 관리하는데 이점들이 있습니다.
Executors.newSingleThreadExecutor의 특징
- 스케줄링한 작업은 큐에 쌓이고 하나의 스레드에서 순차적으로 작업을 처리한다.
- 스케줄링은 execute 또는 submit 사용해서 작업을 큐에 쌓는다.
- submit은 스케줄링한 작업의 결과를 확인할 수 있는 Future를 반환하고,
execute는 작업의 결과를 기다리지 않고 즉시 반환한다.
아래 예시처럼 Executors.newSingleThreadExecutor를 사용하면 단일 스레드 특성상 연속적으로 호출되더라도
하나의 스레드에서 작업이 순차적으로 처리되는 게 보장됩니다.
private val singleThreadExecutor = Executors.newSingleThreadExecutor()
private fun executorExample() {
// 연속적으로 호출이되도 큐에 쌓인 작업이 하나의 스레드에서 순차적으로 처리 (중복 실행 X)
singleThreadExecutor.execute {
var count = 0
while (count <= 5) {
println("currentThread(${Thread.currentThread().name}), count $count")
count++
Thread.sleep(1000)
}
}
}
또한, submit을 통한 스케줄링은 작업의 결과를 확인할 수 있는 Future를 반환해 주기 때문에
작업 중에 큐에 추가되는 걸 원치 않는다면 아래와 같이 처리도 가능합니다.
private val singleThreadExecutor = Executors.newSingleThreadExecutor()
private var future: Future<*>? = null
private fun executorExample() {
if (future?.isDone == false) { // 작업 중에는 큐에 추가되지 않도록 처리
println("Thread is already running.")
return
}
future = singleThreadExecutor.submit {
var count = 0
while (count <= 5) {
println("currentThread(${Thread.currentThread().name}), count $count")
count++
Thread.sleep(1000)
}
}
}
코루틴(Coroutine) 중복 실행 방지 : Job, suspend
코틀린에서 비동기 작업으로 코루틴을 빼면 서운할 수 있습니다.
코루틴 특징
- launch 또는 async를 사용해서 비동기적으로 실행할 코드 블록을 정의한다.
- launch는 비동기 작업을 관리할 수 있는 Job 객체를 반환한다.
- async는 비동기 작업을 관리할 수 있는 Deferred 객체를 반환한다.
- async는 suspend와 Deferred.await를 사용해서 비동기 작업을 순차적으로 처리할 수 있다.
launch는 비동기 작업을 관리할 수 있는 Job 객체를 반환하며 해당 객체를 통해서 중복 실행 방지를 할 수 있습니다.
private val coroutineScope = CoroutineScope(Dispatchers.Default)
private var job: Job? = null
private suspend fun coroutineExample() {
if (job?.isActive == true) { // 중복 실행 방지
println("Coroutine is already running.")
return
}
job = coroutineScope.launch {
var count = 0
while (count <= 5) {
println("currentThread(${Thread.currentThread().name}), count $count")
count++
delay(1000)
}
}
}
마찬가지로 async를 사용했을 때 Deferred 객체를 반환받고 중복 실행에 대한 처리가 가능합니다.
private val coroutineScope = CoroutineScope(Dispatchers.Default)
private var deferred: Deferred<Boolean>? = null
private fun coroutineExample() {
if (deferred?.isActive == true) { // 중복 실행 방지
println("Thread is already running.")
return
}
deferred = coroutineScope.async {
var count = 0
while (count <= 5) {
println("currentThread(${Thread.currentThread().name}), count $count")
count++
delay(1000)
}
true // 비동기 작업이 완료에 대한 결과값 true 반환
}
}
코루틴에서 suspend와 await를 함께 사용하면 연속적으로 호출해도 해당 비동기 작업은 순차적으로 처리되기 때문에
중복 실행에 대한 오류를 피할 수 있습니다.
private val coroutineScope = CoroutineScope(Dispatchers.Default)
private var deferred: Deferred<Boolean>? = null
private suspend fun coroutineExample(): Boolean? {
deferred = coroutineScope.async {
var count = 0
while (count <= 5) {
println("currentThread(${Thread.currentThread().name}), count $count")
count++
delay(1000)
}
true // 비동기 작업 완료에 대한 결과값 true 반환
}
return deferred?.await() // 비동기 작업이 끝날 때까지 대기하고 결과 값 반환
}
'안드로이드 > 코틀린' 카테고리의 다른 글
안드로이드 WiFi 소켓 통신 (예제 다운로드) (0) | 2024.11.16 |
---|---|
안드로이드 화면 전환: Navigation 기능 사용법 및 예제 (2) | 2024.07.13 |
안드로이드 소수점 표기 올림? 버림? : String.format, DecimalFormat (0) | 2024.05.10 |
코틀린 데이터 변경 감지 : 프로퍼티 위임(Delegated Properties) (0) | 2024.05.08 |
앱 백그라운드 상태 체크 : LifecycleEventObserver, DefaultLifecycleObserver (0) | 2024.05.07 |
댓글