시배's Android

Kotlin 동시성 프로그래밍 | 1장. Hello, Concurrent World! 본문

Book/Kotlin 동시성 프로그래밍

Kotlin 동시성 프로그래밍 | 1장. Hello, Concurrent World!

si8ae 2023. 7. 9. 20:38

프로세스

  • 프로세스는 실행 중인 애플리케이션의 인스턴스다.
  • 애플리케이션이 시작될 때마다 애플리케이션의 프로세스가 시작된다.
  • 프로세스는 상태를 갖고 있다.
  • 리소스를 여는 핸들, 프로세스 ID, 데이터, 네트워크 연결 등은 프로세스 상태의 일부이며 해당 프로세스 내부의 스레드가 액세스를 할 수 있다.

스레드

  • 실행 스레드는 프로세스가 실행할 일련의 명령을 포함한다.
  • 프로세스는 최소한 하나의 스레드를 포함하며 이 스레드는 애플리케이션의 진입점을 실행하기 위해 생성된다.
  • 보통 진입점은 애플리케이션의 main() 함수이며 메인 스레드라 하는데 프로세스의 라이프 사이클과 연관된다.
  • 각 스레드는 스레드가 속한 프로세스에 포함된 리소스를 액세스하고 수정할 수 있지만 스레드 로컬 스토리지 라는 자체 저장소도 갖고 있다.
  • 애플리케이션이 사용자 경험에 부정적인 영향을 미칠 수 있는 스레드는 블로킹하지 않아야 한다.
  • GUI 애플리케이션은 애플리케이션의 응답성을 항상 유지하기 위해 UI 스레드를 블록하지 않는다.

코루틴

  • 코틀린 문서에서는 코루틴을 경량 스레드라고도 한다.
  • 코루틴은 스레드 안에서 실행된다.
  • 같은 스레드에 10개의 코루틴이 있다면 해당 시점에는 하나의 코루틴만 실행된다.
suspend fun createCoroutines(amount: Int) {
    val jobs = ArrayList<Job>()
    for (i in 1..amount) {
        jobs += launch {
            delay(1000)
        }
    }
    jobs.forEach {
        it.join()
    }
}
  • 해당 함수는 파라미터 amount에 지정된 수만큼 코루틴을 생성해 각 코루틴을 1초 간 지연시킨 후 모든 코루틴이 종료될 때까지 기다렸다가 반환한다.
  • 테스트 환경에서 amount를 10,000으로 실행할 때 약 1,160ms가 걸리는데 반해 100,000으로 실행하는 데 1,649ms가 소요됐다.
  • 코틀린은 고정된 크기의 스레드 풀을 사용하고 코루틴을 스레드들에 배포하기 때문에 실행 시간이 매우 적게 증가한다.
  • 스레드는 한 번에 하나의 코루틴만 실행할 수 있기 때문에 프레임워크가 필요에 따라 코루틴을 스레드들 사이에 옮기는 역할을 한다.

동시성과 병렬성

  • 동시성은 두 개 이상의 알고리즘의 실행 시간이 겹쳐질 때 발생한다. 중첩이 발생하려면 두 개 이상의 실행 스레드가 필요하다. 이런 스레드들이 단일 코어에서 실행되면 병렬이 아니라 동시에 실행되는데, 단일 코어가 서로 다른 스레드의 인스트럭션을 교차 배치해서, 스레드들의 실행을 효율적으로 겹쳐서 실행한다.
  • 병렬은 두 개의 알고리즘이 정확히 같은 시점에 실행될 때 발생한다. 이것이 가능하려면 2개 이상의 코어와 2개 이상의 스레드가 있어야 각 코어가 동시에 스레드의 인스트럭션을 실행할 수 있다. 병렬은 동시성을 의미하지만 동시성은 병렬성이 없이도 발생할 수 있다는 점에 유의하자.

동시성이 어려운 이유

  • 레이스 컨디션
  • 원자성 위반
  • 교착 상태
  • 라이브 락

코틀린에서의 동시성

  • 스레드의 실행을 블로킹하지 않으면서 실행을 잠시 중단
  • 스레드를 블록하지 않고 동시성 코드를 효과적으로 통신하고 동기화하는 메커니즘 제공 (채널, 액터, 상호 배제)
  • 명시적인 선언
  • 코틀린의 동시성 코드는 순차적 코드만큼 읽기 쉽다.
  • newSingleThreadContext()를 통해 스레드 생성 가능
  • newFixedThreadPoolContext() 스레드 풀 생성 가능
  • 코루틴을 다른 스레드로 이동시키는 역할은 런타임이 담당
  • 유연성
    • 채널 : 코루틴 간에 데이터를 안전하게 보내고 받는 데 사용할 수 있는 파이프
    • 작업자 풀 : 많은 스레드에서 연산 집합의 처리를 나눌 수 있는 코루틴의 풀
    • 액터 : 채널과 코루틴을 사용하는 상태를 감싼 래퍼로 여러 스레드에서 상태를 안전하게 수정하는 메커니즘을 제공
    • 뮤텍스 : 크리티컬 존영역을 정의해 한 번에 하나의 스레드만 실행할 수 있도록 하는 동기화 메커니즘
    • 스레드 한정 : 지정된 스레드에서만 실행하도록 하는 기능
    • 생성자 : 필요에 따라 정보를 생성할 수 있고 새로운 정보가 필요하지 않을 때 일시 중단될 수 있는 데이터 소스