인프런 커뮤니티 질문&답변

가나다라님의 프로필 이미지
가나다라

작성한 질문수

2시간으로 끝내는 코루틴

코루틴을 잘 사용하고 있는지 궁금합니다.

작성

·

61

·

수정됨

0

강사님 강의를 듣고 배치 단위로 요청을 가져와 Redis 서버에서 작업을 처리하는 부분에 코루틴을 적용하려고 합니다. 강의를 통해 최소한의 스레드로 여러 코루틴을 실행해야 코루틴을 극대화할 수 있다고 이해했습니다. n개의 요청은 모두 독립적이며 배치 단위로 가져온 이유는 1개의 스레드로 여러 코루틴을 실행하기 위함입니다.

 

Redis는 마스터에서만 작업이 가능하므로 요청을 보내고, 응답받아 후속 처리하는 부분을 

  • Redis 서버로 요청 (코루틴1)

  • Redis 서버로부터 응답받아 후속 처리 (코루틴2)

로 분리했습니다.

1개의 스레드에서 여러 코루틴을 동작시키기 위해 이런 기준으로 코루틴의 중단 지점을 생성하는 것이 적절한지 궁금합니다.

 

또한 n개의 요청을 모두 Redis 서버로 보낸 후 응답 받는 것이 아니라

응답을 받고 후속 처리가 가능한 요청은 ‘모든 요청을 Redis 서버로 전달했다는 여부와 상관없이’ 후속처리를 하기 위해 다음과 같이 코드를 작성했습니다.

 

coroutineScope {
   val jobs = userIds.map { userId ->
       async {
           // main 스레드가 처리
           val result = 레디스 접근 메서드 (RedisTemplate)
           Pair(userId, result)
       }
   }
   jobs.forEach { job ->
       launch(Dispatchers.IO) {
           val (userId, status) = job.await()
           // 응답에 대한 후속 처리
            // 결과마다 다른 DefaultDispatcher-worker 가 처리
       }
   }
}

위와 같이 작성하면 Redis로 요청을 보내는 부분은 1개의 main스레드가 처리하지만, 후속 처리는 결과마다 다른 DefaultDispatcher-workder가 처리합니다. 이는 결과마다 다른 스레드가 처리하는 것이 맞다면 이 코드는 코루틴을 제대로 사용하지 못하고 있다고 생각하는데 제 생각이 맞는지 궁금합니다.


coroutineScope {
   launch {
       for(userId in userIds) {
           val result = 레디스 접근 메서드
           channel.send(Pair(userId, result))
       }
       channel.close()
   }
   launch {
       for(result in channel) {
           // 응답에 대한 후속 처리
       }
   }
}

위와 같이 작성하면 1개의 main 스레드가 모든 작업을 처리합니다. 이렇게 작성해야 코루틴을 제대로 사용하는 것이 맞는지 궁금합니다.

 

강의 잘 들었고 감사합니다!

답변 1

0

최태현님의 프로필 이미지
최태현
지식공유자

안녕하세요 가나다라님! 🙂 질문 주셔서 감사합니다. 어떤 작업을 하시는지 배경지식이 제한된 상태로 답변 드려보면 제 생각은 다음과 같습니다.

적어주신 내용에 대해 하나씩 말씀드려 보겠습니다.

 

강의를 통해 최소한의 스레드로 여러 코루틴을 실행해야 코루틴을 극대화할 수 있다고 이해했습니다.

결과마다 다른 스레드가 처리하는 것이 맞다면 이 코드는 코루틴을 제대로 사용하지 못하고 있다고 생각하는데

  • 두 멘트가 서로 연관이 있어 함께 답변 드리겠습니다.

  • 이론상 적은 스레드를 여러 코루틴으로 실행시킬 경우 코루틴의 효과를 극대화 할 수 있는 것이 맞습니다.

    다만 그러한 행위 자체가 코루틴을 제대로 사용하고 있는지, 사용하고 있지 못한지에 대한 판단 기준은 아니라고 생각합니다.

  • 여러 작업을 동시에 실행시키고, CPU 작업이 필요할 때만 적은 스레드를 배정하는 non-blocking 방식은 코루틴의 목표라기 보다, 오히려 그런 non-blocking /비동기 작업을 동기 작업 처럼 가독성 좋은 코드로 구현할 수 있게 해주는 것이 코루틴의 목표에 가깝다고 생각합니다.

 

이런 관점에서 배치 단위로 요청을 가져와 Redis 서버에서 작업을 처리 해야 한다면... 적어주신 코드를 볼 때

  • scheduler 작업인건지, Batch 작업인건지, API 작업인건지

  • API 작업이라면 MVC 같은 thread-per-request 구조인지 webflux 구조인지

에 따라

  • 코루틴을 사용하는 것이 좋을지, 사용하지 않는 것이 좋을지

  • 코루틴을 어떤 방식으로 사용하는 것이 좋을지

가 달라진다고 생각합니다.

 

만약 webflux + coroutine 조합이라면 스레드가 한 개인지 두 개인지를 크게 신경쓰지 않고 다음과 같이 코드를 작성해도 문제가 없다고 생각합니다.

suspend fun handleUserIds(userIds: List<Long>) {
  userIds.forEach {
    val redisResult = 레디스 접근 메소드

    // channel이 무엇을 의미하는지 정확히 모르겠군요!
    // 첫 번째 코드에는 channel 코드가 없고 두 번째 코드에만 있어서요
    val result = channel.send(Pair(userId, redisResult.await()))
    
    // 후속 처리
  }
}

 


위와 같이 작성하면 1개의 main 스레드가 모든 작업을 처리합니다

꼭 그렇지는 않습니다. 작성해주신 코드가 어떤 환경에 실행되는지에 따라 여러 스레드로 분산되어 처리될 수도 있습니다.

 

감사합니니다.

가나다라님의 프로필 이미지
가나다라

작성한 질문수

질문하기