withContext로 스레드 전환하기

2024. 9. 18. 21:00·Language/Kotlin
728x90
반응형

저번 글에서 예고했던 대로 메인 스레드에서 했던 무거운 작업을 이제 다른 스레드에서 작업시켜보자.

(이전 글 확인하기)

 

이전 동작 이해하기

아까 첨부한 링크에서의 이전 글을 보면

우리는 viewModelScope를 사용해 CPU 연산이 무거운 작업을 메인 스레드에서 실행했다.

 

그렇게 수초간 메인 스레드가 blocking되었고, 다른 UI는 반응할 수 없었다.

 

그리고 우리가 실행했던 viewModelScope의 Context를 확인하는 방법이 하나 있다.

fun performCalculationOnMain(factorial: Int) {
	viewModelScope.launch {
    	println("Coroutine Context: $coroutineContext")
    	var result = BigInteger.ONE
        val computationDuration = measureTimeMillis {
        	result = calculateFactorial(factorial)
        }
        
        var resultInString = ""
        val conversionDuration = measureTimeMillis {
        	resultInString = result.toString()
        }
        
        println("calculation: $computationDuration, conversion: $conversionDuration")
    }
}

이렇게 println을 사용해서 해당 코루틴의 Context를 확인할 수 있는데..

 

... [StandaloneCoroutine{Active}@1234567, Main[immediate]]

 

이 프린트문을 통해 해당 코루틴이 Active 상태임을 알 수 있고

메인 스레드에서 실행되고 있음을 확인할 수 있다.

 

그냥 viewModelScope에서 Dispatcher를 바꾼다면?

viewModelScope.launch(context = Dispatchers.Default) {
	// ...
}

물론 이렇게 수정한다면 현재 예제에서는 괜찮을 수 있다.

 

하지만, 보통 연산 결과를 UI로 보여주기 위해서는 LiveData의 값을 setValue를 통해 변경해주게 되는데,

 

이 작업은 메인 스레드에서만 수행해야 하므로 위 코드는 적절하지 않다.

 

다른 스레드에서 실행하기 위해 withContext 사용하기

이제 우리가 전에 사용했던 calculateFactorial 함수를 변경해보자.

 

먼저 함수 본문을 withContext로 감싼다.

 

dispatcher를 지정한다.

 

그렇게 되면 빨간 밑줄이 withContext에 생기게 되는데

이는 코루틴 내부나 suspend function 내부에서 호출하지 않았다는 의미의 에러이다.

suspend fun calculateFactorial(number: Int): BigInteger {
	withContext(Dispatchers.Default) {
    	var factorial = BigInteger.ONE
    	for (i in 1 .. number) {
    		factorial = factorial.multiply(BigInteger.valueOf(i.toLong()))
    	}
    	return factorial
	}
}

이렇게 되면 return에서 에러가 발생하는데 withContext를 리턴해주어야 한다.

 

그리고 withContext 내부에서는

마지막 줄이 자동으로 return값이 되기 때문에 return 키워드를 지워주면 된다.

 

완성된 함수는 아래와 같다.

suspend fun calculateFactorial(number: Int): BigInteger {
	return withContext(Dispatchers.Default) {
    	var factorial = BigInteger.ONE
    	for (i in 1 .. number) {
    		factorial = factorial.multiply(BigInteger.valueOf(i.toLong()))
    	}
    	factorial
	}
}

 

해당 함수를 다른 문법으로도 정리할 수 있다.

suspend fun calculateFactorial(number: Int) = withContext(Dispatchers.Default) {
    	var factorial = BigInteger.ONE
    	for (i in 1 .. number) {
    		factorial = factorial.multiply(BigInteger.valueOf(i.toLong()))
    	}
    	factorial
	}
}

이렇게 되면 withContext 내부에서 실행되는 코드 블럭은 Default Dispatcher를 통해 실행된다.

 

욕심내서 문자열 변환도 다른 스레드에서 실행하기

위에서 봤던 performCalculationOnMain 함수에서

연산 결과를 toString으로 문자열 변환하는 부분이 있었다.

 

해당 부분에 withContext를 사용해보자.

val conversionDuration = measureTimeMillis {
	resultInString = withContext(Dispatchers.Default) {
		result.toString()
	}
}

이제 문자열 변환도 메인이 아닌 다른 스레드에서 실행될 것이다.

 

withContext는 정말 Context를 바꾼다!

내 예제에서는 Dispatcher만 변경해서 사용했지만 ExceptionHandler나 Job을 변경해서 사용할 수 있다.

 

하지만, 보통 Dispatcher이외의 Context 요소를 변경할 일은 거의 없다고 한다.

 

그럼에도 불구하고 코루틴 이름을 같이 변경한다면 + 연산자를 사용해 동시에 변경할 수 있다.

withContext(Dispatchers.Default + CoroutineName("String Converter")) {
	// ...
}

 

물론 당연하게도 같은 요소를 두 번 변경할 수 없다.

withContext(Dispatchers.Default + Dispatchers.IO) {
	// ...
}

가령 위와 같은 코드는 에러가 발생한다.

728x90
반응형
저작자표시 비영리 변경금지

'Language > Kotlin' 카테고리의 다른 글

Java, Kotlin Interoperability 해결하기  (0) 2024.11.18
CoroutineScope와 CoroutineContext 되짚어보기  (0) 2024.09.21
Coroutines Dispatcher 알아보기  (2) 2024.09.13
CoroutineContext와 CoroutineScope 알아보기  (1) 2024.09.04
Coroutines on Main Thread(위험성에 대해 인지하기)  (0) 2024.09.03
'Language/Kotlin' 카테고리의 다른 글
  • Java, Kotlin Interoperability 해결하기
  • CoroutineScope와 CoroutineContext 되짚어보기
  • Coroutines Dispatcher 알아보기
  • CoroutineContext와 CoroutineScope 알아보기
BonusTrack02.dev
BonusTrack02.dev
공부, 일상
  • BonusTrack02.dev
    BonusTrack02.dev
    BonusTrack02.dev
  • 전체
    오늘
    어제
    • 분류 전체보기 (237) N
      • Android (84)
      • Language (63) N
        • Java (19)
        • Kotlin (27) N
        • Swift (17)
      • 프로그래머스 (68)
      • 주저리주저리 (22)
        • 카페 (5)
        • 음식점 (4)
        • 컨퍼런스 (1)
        • 팝업스토어 (4)
        • 해외여행 (0)
        • 전시회 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    databinding
    MVVM
    coroutines
    오블완
    스위프트
    ViewModel
    코틀린
    프로그래머스
    Kotlin
    티스토리챌린지
    CodeLab
    android
    배열
    Material
    room
    viewModelScope
    코루틴
    Java
    daterangepicker
    자바
    Observer
    getNumericValue
    LiveData
    jetpack
    aac
    ios
    PCCE
    programmers
    SWIFT
    안드로이드
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
BonusTrack02.dev
withContext로 스레드 전환하기
상단으로

티스토리툴바