개발자의 오르막
고루틴과 동시성 프로그래밍 본문
Keyword
golang , goroutine
고루틴이란 ?
- 고루틴(goroutine) 은 경량스레드로, 프로그램에 있는 다른 고루틴과 관련하여 독립적으로 동시에 실행되는 함수이다.
- 가령, 메시지 전송과 같은 이벤트를 처리할 때, 하나의 스레드로 메시지를 전송시킬 수도 있지만 여러개의 쓰레드를 동시에 동작시켜 메시지를 전송할 수 있다. 각각의 쓰레드는 독립적으로, 동시에 실행되기 때문에 메시지를 빠르게 전송시킬 수 있으며, 특정 메시지가 오류가 나더라도 다른 정상적인 메시지들은 정상적으로 전송 시킬 수 있다.
- 고루틴은 Java 의 thread 보다 더 경량화된 Thread이다.
- 더 적은 메모리 할당하며 성능이 좋다.
- Golang 언어에서 자체적으로 지원해주는 Go scheduler 에서 관리해준다.
- 고루틴은 Main 함수의 고루틴이 새로운 서브 고루틴을 생성하는 형태로 이루어진다.
- 이때 Main 함수의 고루틴이 종료되면 서브 고루틴들도 종료된다.
프로세스와 스레드의 개념
- 프로세스란 ?
- 프로세스는 메모리 공간에 로딩되어 동작하는 프로그램이다.
- 프로세스는 한 개 이상의 스레드를 가지고 있다.
- 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원, 스레드로 구성된다.
- 스레드란 ?
- 스레드는 프로세스 내에서 실제로 작업을 수행하는 주체를 의미한다.
- 스레드가 1개이면 싱글 스레드 프로세스, 스레드가 여럿이면 멀티 스레드 프로세스이다.
- 테스킹
- 한 번에 한 프로세스만 동작시키는 것을 싱글 테스킹
- 여럿을 동시에 동작시키는 것을 멀티 테스킹
고루틴은 어떤 이점으로 사용되는가 ?
- 컨텍스트 스위칭 비용이란 ?
- CPU 코어는 한 번에 한 명령만 수행할 수 있음
- 그러나 단일코어에서도 스레드를 빠르게 전환해가며 수행하면, 사용자 입장에서는 여러 프로그램을 동시에 수행하는 것으로 보임
- 그러나 CPU 코어가 여러 스레드를 전환하면서 수행하려면 스레드의 현재 상태, 스레드의 명령 포인터, 스택 메모리 등의 정보를 저장해야 하는데, 이를 스레드 컨텍스트라 함.
- 스레드 컨텍스트가 보존되어야 다른 스레드가 전환되어 돌아올 때 마지막 실행한 상태부터 이어서 스레드를 실행할 수 있기 때문
- 결국 스레드가 전환될 때마다 스레드 컨텍스트를 저장하고 복원해야 하기 때문에 스레드 전환비용이 발생함. 이를 컨텍스트 스위칭 비용이라 함
- Go 에서는 CPU 코어마다 OS 스레드를 하나만 할당해서 사용하기 때문에 위와 같은 컨텍스트 스위칭 비용이 발생하지 않음
고루틴의 사용 방법
- Go 언어에서 모든 프로그램은 고루틴을 최소한 하나는 가지고 있습니다. ( main() 함수 )
- 고루틴은 main() 함수와 함께 시작되고, main() 함수가 종료되면 같이 종료됩니다.
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func SumAtoB(a, b int) {
sum := 0
for i := a; i <= b; i++ {
sum += i
}
fmt.Printf("%d부터 %d까지 합계는 %d입니다.\n", a, b, sum)
wg.Done()
}
func main() {
wg.Add(10)
for i := 0; i < 10; i++ {
go SumAtoB(1, 100000000)
}
wg.Wait()
fmt.Println("모든 계산이 완료됐습니다.")
}
- sync 패키지의 WaitGroup 객체를 사용
- 이 객체는 여러 개의 고루틴이 모두 작업이 종료될 때 종료될 수 있도록 제어할 수 있는 객체입니다.
- wg.Add(10) 은 10개의 작업 개수를 설정한다는 의미
- wg.Wait() 는 wg 의 작업이 모두 종료될 때까지 대기한다는 의미
- wg.Done() 이 호출되는 시점에 해당 작업이 종료된다는 의미입니다.
- 따라서 wg 의 작업 10개가 모두 종료되어야 모든 계산이 완료되었다는 문구가 출력될 수 있습니다.
- 이 객체는 여러 개의 고루틴이 모두 작업이 종료될 때 종료될 수 있도록 제어할 수 있는 객체입니다.
Goroutine 주의사항
func main() {
fmt.Println("go routine 시작 전")
var wg sync.WaitGroup
wg.Add(10)
for i:=0; i<10; i++ {
go func() {
fmt.Println("go routine 진행중", i)
wg.Done()
}(i)
}
wg.Wait()
fmt.Println("go routine 끝")
}
- wg.Add(10) 처럼 waitGroup 초기화는 로직 처리 전에 해주어야 한다. (go func 전에)
- 고루틴 함수 내에서 사용하는 변수는 고루틴을 생성하는 시점의 값을 넣어주어야 한다.
고루틴의 장점
- 컨텍스트 스위칭 비용이 발생하지 않게 됩니다.
- 컨텍스트 스위칭은 CPU 코어가 스레드를 변경할 때 발생하는데 고루틴을 이용하면 코어와 스레드는 변경되지 않고 오직 고루틴만 옮겨 다니기 때문입니다.
- 코어가 스레드를 변경하지 않기 때문에 컨텍스트 스위칭 비용이 발생하지 않습니다.
'GoLang' 카테고리의 다른 글
Go, Makefile, Docker로 프로젝트 셋팅 및 빌드 정보 활용하기 (0) | 2022.10.08 |
---|---|
Golang 과 TDD (0) | 2022.10.02 |
golang과 Docker로 환경변수 제어하기 (0) | 2022.08.23 |
GoLang 의 포인터 (0) | 2022.07.03 |
redis: discarding bad PubSub connection: redis: ping timeout 이슈 (0) | 2022.06.17 |
Comments