고루틴(goroutine)에 알아보자
서론
회사에서 Golang로 많을 것을 만들어왔고 유지 보수를 해왔던 나지만 아무 생각없이 고루틴을 쓰고 있기 떄문에 이번 기회에 고루틴에 대해서 조금이나마 정확히 알기 위해서 기록합니다.
고루틴(goroutine)이란?
- 고루틴은 Golang 런타임에 의해 관리되는 동시성 프로그래밍을 위한 경량 스레드 을 의미합니다.
- 스케줄러는 M:N 스케줄링을 사용하여 M개의 고루틴을 N개의 OS 스레드에 매핑합니다.
- 고채널을 통해 고루틴간의 통신을 할 수 있습니다.
Go에서 실행되는 모든 프로그램들은 고루틴에서 실행됩니다. (메인 함수도 고루틴에서 실행이 됩니다. 메인 고루틴이 종료하게 되면 즉시 다른 고루틴들이 종료됩니다.)
주의할 점
CPU의 스레드와 OS 영역에서 다뤄지는 스레드는 서로 지칭하는 대상이 다릅니다.
CPU의 스레드는 한개의 코어를 OS에게 여러개로 인식시켜 동작하도록 하는 하드웨어 영역의 개념이고, 고루틴에서 사용하는 스레드는 OS 영역에서 다뤄지는 스레드로 OS 하위의 소프트웨어 영역에서 CPU의 작업단위로 지칭되는 용어입니다.
고루틴 관리
런타임 스케쥴러는 다음과 같은 원칙을 가지고 동작합니다.
- 커널 스레드는 비싸기 때문에 되도록 작은 수를 사용
- 많은 수의 고루틴을 실행하여 높은 동시성를 유지한다.
- N 코어 머신에서, N 개의 고루틴을 병렬하게 동작시킨다.
주의할 점
동시성과 병렬성은 서로 비슷한 개념이라고 생각할 수 있지만 명확히 다른 개념이다.
동시성은 싱글 코어에서 멀티 스레드를 동작시키는 논리적인 개념으로 한번에 여러개가 동시에 실행되는 것 처럼 보이게 된다.
병렬성은 물리적으로 동시에 여러작업을 처리할 수 있기 때문에 멀티 코어에서 멀티 스레드를 동작시키는 방식이다.(Go 1.5 버전 이전에는 CPU 코어 하나만 사용하도록 기본 설정되어 있었다. 하지만 현재는 물리 CPU 개수만큼 사용하도록 설정되어 각 코어에서 시분할처리로 동작한다. 만약에 여러 CPU를 병렬로 실행하고자 하는 코드를 작성하고자 한다면 runtime.GOMAXPROCS(runtime.NumCPU()) 함수를 호출하여 시스템의 모든 코어를 사용하도록 설정할 수 있습니다.)
고루틴은 스레드에 비해 더 작은 메모리를 필요로 한다. (고루틴 2KB, 스레드 1MB)
스레드는 OS에 리소스(메모리)를 요청하고 다 사용하면 반환해야 하기 때문에 상당한 생성, 소멸 비용이 들어간다.
반면에 Go 런타임 스케줄러에 의해 생성 및 소멸된다.
고루틴의 특징
다른 언어들과의 동시성 쓰레드 간단 비교
구분 | 고루틴 | 자바 가상 쓰레드 | 코틀린 코루틴 | 파이썬 코루틴 |
---|---|---|---|---|
언어 | Go | 자바 19 이상 | 코틀린 | 파이썬 |
스레드 | 무게 매우 가벼움 | 가벼움 | 중간 정도 | 중간 정도 |
메모리 사용 | 적음 | 적음 | 중간 정도 | 중간 정도 |
채널 통신 | 지원 | 지원 안 함 | 지원 (Flow) | 지원 (async/await) |
협력적 스케줄링 | 지원 | 지원 안 함 | 지원 (suspend/resume) | 지원 (async/await) |
자동 관리 | Go 런타임 | 가상 스레드 풀 | 코루틴 라이브러리 | asyncio 라이브러리 |
호환성 | Go 코드만 | 기존 자바 코드와 호환 | 코틀린 코드만 | 파이썬 3.5 이상 |
제약 | 없음 | 데몬 스레드만 가능 | 일부 기능 제한 (예: Java NIO) |