작성
·
383
0
안녕하세요 강사님, 좋은 강의에 항상 감사드리고 있습니다!
다른게 아니라, 강의 중 suspend function은 일시 중단 되었다가 재개될 수도 있고 / 그렇지 않을 수도 있다고 설명 주셨습니다.
그렇다면 실제로 suspend function이 일시 중단되어야 하는지
여부는 어떻게 결정되나요?
단순히 suspend function 내부에서 또 다른 suspend function을 호출하기만 하면 무조건 일시 중단 되는 것인지, 아니면 Node.js 진영의 libuv 라이브러리처럼 비동기적으로 처리되어야 하는 IO 작업을 내부적으로 인지하는 방식이 별도로 구현되어 있는지 궁금합니다!
답변 2
2
저도 Coroutine을 배우는 입장에서 이 부분이 조금 헷갈렸어서 추가적으로 글 작성해보겠습니다!
Kotlin docs의 coroutines 기본을 보다보면 launch에 대한 설명이 나옵니다.
https://kotlinlang.org/docs/coroutines-basics.html#your-first-coroutine
즉, launch는 새로운 Coroutine 만들어냅니다.
Coroutine을 대게 경량 쓰레드라고도 표현하니까 쓰레드로 예시를 들면 쓰레드 t1, t2가 경합하면 어떤것이 먼저 실행될까요? 아마 태현님이 말씀하신것처럼 OS나 내부 구현에 따라 달라질 것 같습니다.
저는 Spring 환경에서 테스트를 해보고있어서 다음과 같은 형식으로 코드를 작성했는데 임의로 delay() 메서드나 yield()로 요리조리 해보면 다양한 결과를 만날 수 있어서 이해가 조금 편했던것 같습니다.
@Component
class CoroutinesLecture1: ApplicationRunner {
override fun run(args: ApplicationArguments?) {
runBlocking {
println("START")
launch {
newRoutine()
}
yield()
println("END")
}
}
suspend fun newRoutine(){
val num1 = 1
val num2 = 2
println("${num1 + num2}")
}
}
yield로 위임했기 때문에 START -> 3 -> END 결과
@Component
class CoroutinesLecture1: ApplicationRunner {
override fun run(args: ApplicationArguments?) {
runBlocking {
println("START")
launch {
newRoutine()
}
yield()
println("END")
}
}
suspend fun newRoutine(){
val num1 = 1
val num2 = 2
delay(50)
println("${num1 + num2}")
}
}
yield로 위임했지만 delay 메서드를 만났기 때문에 일시중단되어 START -> END -> 3 결과
글을 작성하다보니 저도 한가지 궁금한점이 생겼서 질문드립니다!
현재는 코루틴이 2개여서 중단과 재개가 눈에 잘 보이는 편이지만 만약 현업에서 코루틴이 N개 이상인 경우에서는 이런 과정들이 파악하기 힘들 것 같은데 코루틴끼리는 항상 동시에 실행될 것이라고 가정하고 코드를 작성하도록 컨벤션등이 있는지 궁금합니다!
틀린정보이거나 헷갈리는 부분있으시면 말씀해주세요!
늘 질좋은 강의 제공해주셔서 감사합니다ㅎㅎ
제가 궁금하던 부분의 핵심을 대답해주셔서 감사합니다!
말씀해주신 것 처럼 멀티 쓰레드인 Spring 환경에서 사용한다고 가정했을 때 코루틴은 여러 쓰레드에 점유될 가능성이 높으니 race-condition 문제를 잘 해결해주어야 겠네요(물론 기존에도 잘 고려해주어야 하지만)
감사합니다!
1
안녕하세요! 잉G님! 정말 좋은 질문 감사드립니다! 😊
결론부터 말씀드리면, "suspend function이 호출되었을 때 중단될 필요성이 있으면 중단된다" 입니다. 예를들어 아래와 같은 코드를 실행시키면,
fun main(): Unit = runBlocking() {
for (i in 1..2) {
// START 출력
newRoutine(i)
// END 출력
}
}
suspend fun newRoutine(i: Int) {
// MIDDLE 출력
}
첫 번째 루프에서 START를 출력한 이후 중단 함수인 suspend fun이 호출되었으므로, 잠시 중단되고 두 번째 루프인 START가 먼저 출력될 수 있어 보이지만
실제로는 첫 번째 루프에서 START -> MIDDLE -> END가 모두 출력되고, 두 번째 루프의 START -> MIDDLE -> END가 출력되게 됩니다.
결론적으로는 중단될 필요성이 있으면 중단된다 라고 봐주시면 될 것 같습니다! 😊
답변이 도움이 되었으면 좋겠습니다. 감사합니다!! 🙏
강사님, 친절한 답변 감사합니다!
제가 아무래도 질문을 잘 못 드린 것 같아요, 제가 궁금한게 바로 그 중단될 필요성
이었습니다...!
예시로 들어주신 코드의 경우, 머신이 코드를 실행할 때 newRoutine에서 중단할 필요가 없다고 판단하고 계속 실행한 것으로 보이는데, 요런 중단될 필요성
을 판단하는 기준이 별도로 있는건가요?
안녕하세요 잉G님!! 중단된 필요성
말씀이시군요!!
우선 제가 알고 있기로는 코루틴 내부 로직에 따른다고 알고 있긴 합니다! OS에서 프로세스를 스케쥴링 하는 것과 비슷한 느낌으로요! OS를 생각해보면, 저희가 A 프로세스 다음 어떤 프로세스가 실행될지 정확히 알 수 없죠!
어떤 코루틴을 중단시키고, 어떤 코루틴을 재개 시킬지는 코루틴의 내부 스케쥴러 구현에 따라 다르다 정도로 알아주시면 좋을 것 같습니다!
이와 관련해서 혹시나 저도 더 정보를 알게된다면, 꼭 추가 영상을 통해 안내해 드리도록 하겠습니다. 오늘도 좋은 하루 되세요! 감사합니다!! 🙏
안녕하세요! junuuu님! 😊 추가적으로 좋은 댓글 남겨주셔서 감사합니다! 😊 👍
에 대해서 답변 드려 보자면,
"상황에 따라 조금 다를 수 있지만 대체로 동시에 실행될 수 있다는 것을 감안하고 코드를 작성한다"로 정리드릴 수 있을 것 같습니다.
예를 들어, 코루틴이 실행되려면 결국 "스레드"가 존재해야 하다 보니, 제가 수십개의 코루틴을 한 개의 스레드에서만 확정적으로 돌린다고 생각하고, 한 코루틴에 중단지점이 존재하지 않는다면 (혹은 blocking API만을 사용한다면) 여러 코루틴이 동시에 (concurrently) 돌아갈 경우가 없기 때문에 동시에 실행된다는 가정을 하지 않고 코드를 작성할 수 있습니다.
하지만 만약 멀티 스레드를 통해 여러 코루틴을 돌린다거나, 한 스레드에서 중단 지점이 존재하는 코루틴들을 돌린다면, 코루틴들이 동시에 돌아갈 수 있다 보니 thread-safe한 코드를 작성해야만 합니다!
thread-safe한 코드는 익히 아시는 것처럼
int
대신AtomicInteger
를 사용한다거나일반 컬렉션 대신 동시성 컬렉션을 사용한다거나
불변 객체나 싱글톤을 활용한다거나 하는 등
의 방법이 있는 것 같습니다! 😊
이렇게 말씀 드려보니 특별한 비법은 없는 것 같네요!! 😅
답변이 도움이 되었으면 좋겠습니다. 감사합니다! 🙇