[kotlin] Corutine & Thread 완벽 분석

Boknami·2024년 2월 2일
0

코틀린

목록 보기
17/19

😀 계기

이전까지는 단순히 코루틴이 비동기적 작업에서 사용된다는 점은 알고 있었다. 그러나 코루틴이 내부적으로 어떻게 동작을 하고 이러한 점이 어떠한 이점이 있는 지 완벽하게 이해한 것은 아니었다. 이번 시간에는 코루틴에 내부 동작을 자세히 알아보며 쓰레드와 비교해보려한다.


🔍 코루틴 = 쓰레드?

우선 코루틴은 쓰레드가 아니다.

🖥 메모리적 측면

우리가 쓰는 프로그램의 흐름을 살펴보자.
최대한 압축하자면 프로그램을 클릭하면 프로그램에 데이터를 메모리에 가져오게 되고 로드된 인스턴스가 프로세스가 된다. 실행된 프로세스는 그 안에서 여러 개의 독립된 흐름인 쓰레드를 만들어서 작동하게 된다.

코루틴은 프로세스의 Heap Memory
쓰레드는 Stack Memory를 독립적으로 할당 받는데.

코루틴은 Stack을 할당받지 않고 프로세스에 할당된 Heap Memory를 공유한다. 즉 메모리 점유 방식에서부터 쓰레드와 같다고 할 수 없는 것이다!


💡 쓰레드에 대해 더 알아보자

왜 heap 에 저장해?

코루틴은 일시 중지된 상태에서 이후에 다시 실행될 수 있어야 하므로, 코루틴의 실행 상태와 관련된 정보는 일반적으로 힙 메모리에 저장

그럼 코루틴은 어디서 동작해?

코루틴은 단일 스레드 내부에서 동작하게 된다. 이게 조금 이해가 어려웠는데, 코루틴이 스레드랑은 별개지만 코루틴 자체는 스레드 안에서 동작하게 된다. 그럼 여기서 안드로이드에 대표적인 스레드들을 알아볼 필요가 있겠다!

안드로이드의 대표적인 스레드

⚫ 메인 스레드(Main Thread):

  • 안드로이드 애플리케이션의 UI를 처리하는 데 사용

  • 화면을 그리고, 이벤트를 처리하며, 사용자 상호 작용에 대한 응답을 담당

  • UI 업데이트는 메인 스레드에서만 수행해야 하며, 메인 스레드가 차단되지 않도록 주의! -> UI에 문제가 생기면 사용자들이 체감될 정도의 문제가 발생! 이런 경우 ANR이란 용어가 존재한다!

  • ANR(Application Not Responding) : UI스레드(메인)이 너무 오랫동안 차단되서 발생

⚫ 백그라운드 스레드(Background Threads):

  • 메인 스레드 이외의 작업을 수행하는 데 사용
  • 네트워크 요청, 데이터베이스 액세스, 파일 I/O 등과 같은 비동기 작업을 처리할 때
  • 일반적으로 별도의 스레드를 사용하여 오래 걸리는 작업을 메인 스레드에서 분리하여 UI의 응답성을 유지

⚫ Handler 스레드:

  • 안드로이드에서 메인 스레드와 백그라운드 스레드 간의 통신에 사용
  • Handler 클래스는 메인 스레드로 작업을 보내거나 지연된 작업을 예약하는 데 사용
  • 메인 스레드에서 실행되며, 다른 스레드에서 작업을 메인 스레드로 보낼 때 사용

⚫ AsyncTask:

  • 백그라운드 스레드에서 실행되는 비동기 작업을 쉽게 처리할 수 있도록 도와줌
  • 백그라운드 스레드에서 실행되는 작업을 수행한 후, 그 결과를 메인 스레드로 전달
  • 안드로이드에서는 이제 AsyncTask 클래스의 사용이 비추천되며, 대신 코루틴이나 다른 비동기 처리 방법을 사용하는 것이 권장

그럼 백그라운드 스레드 쓰면 되잖아? 왜 굳이 코루틴을..?

코루틴이 비동기이면서 스레드 간의 컨텍스트 스위칭 비용이 낮기 때문!

  • 동시성도 있다

첫째, 스레드의 독립성 때문이다!
각 스레드는 독립적인 실행 흐름을 가지고 있다.
스레드는 운영체제에 의해 관리되며, 각 스레드는 고유한 스택과 레지스터 상태를 가지고 있기 때문에 스레드 간의 컨텍스트 스위칭은 스레드의 전체 상태를 저장하고 복원하는 과정이 필요한데 이는 많은 양의 메모리 및 CPU 자원을 소모하게 된다.

둘째, 스레드 간의 동기화 때문이다.
여러 스레드가 공유된 자원에 동시에 액세스하면 동기화 문제가 발생할 수 있다.
결국 이러한 문제를 해결하기 위해 뮤텍스, 세마포어 등의 동기화 메커니즘이 필요하며, 이로 인해 스레드 간의 컨텍스트 스위칭 비용이 증가한다.

반면에 코루틴은 단일 스레드 내에서 실행되는데, 이 때문에 스레드 간의 전환이 아닌 단일 스레드 내에서의 전환만 필요하다. 따라서 코루틴 간의 컨텍스트 스위칭은 현재 스레드의 스택 상태만 저장하고 복원하는 간단한 작업으로 이루어지며, 이는 스레드 간의 컨텍스트 스위칭보다 훨씬 더 경제적일 수 밖에 없다!

그럼 백그라운드 스레드를 버리고 코루틴만 쓰자 어떄?

는...안되겠다ㅠ

코루틴은 많은 경우에 백그라운드 스레드보다 간결하고 효율적인 해결책을 제공할 수 있지만, 특정한 상황에서는 백그라운드 스레드가 더 적합할 수 있다!!

  • CPU 집약적인 작업
  • 긴 지속 시간의 I/O 작업
  • 동시성 요구가 높은 작업

즉, 너무 오래 걸리는 건 그냥 백그라운드 스레드한테 위임하자!


📋 선점여부

  • 코루틴은 선점형, 쓰레드는 비선점형
    • 병행성 : 쓰레드는 멀티코어를 사용해서 동일한 시간에 동시에 2개의 작업이 가능
    • 코루틴은 동시에 작업하는 2개의 작업이 동일한 시간에 수행되진 않는다.
      • 다만 전환 속도가 매우 빠르기 때문에 외부에서는 마치 동시에 수행되는 것처럼
    • 코루틴은 동시성은 있지만 병행성은 없다.

🔍 코루틴의 장점

  • 메모리 오버헤드 감소
    프로세스 내의 코루틴은 동일한 메모리 공간을 공유하므로 각 코루틴에 대한 별도의 메모리 영역 생성 및 유지 관리와 관련된 오버헤드가 줄어든다.
    자체 격리된 메모리 공간을 갖는 여러 프로세스를 만드는 것과 비교할 때 코루틴은 메모리 효율성이 더 높다고 볼 수 있다.

  • 빠른 커뮤니케이션
    코루틴 간의 통신 및 데이터 공유는 복잡한 프로세스 간 통신 메커니즘 없이 힙의 공유 데이터에 직접 액세스할 수 있으므로 더 빠르고 간단해진다.
    이로 인해 여러 코루틴이 협력하고 정보를 교환해야 할 때 더욱 이점이 부각된다.

😥 코루틴의 단점

모든 것이 장점만 있을 수는 없 듯이 코루틴에도 쓰레드와 비교했을 때 여러 단점이 존재한다.

  • 제한된 병렬작업
    위에 언급했듯 코루틴은 본질적으로 병렬이 아니므로 여러 코어에서 병렬 처리가 필요한 작업에서는 좋은 효율은 아니다.

  • 러닝 커브
    비교적 코루틴은 우선적으로 쓰레드나 프로세스 같은 CS 지식이 잘 이루어진 뒤에 학습할 수 있다고 생각한다. 코루틴으로 효율적인 흐름을 설계하고 수명 주기를 관리하는 일은 어려운 일이라고 생각한다.

0개의 댓글