개발자의 오르막

고루틴과 동시성 프로그래밍 본문

GoLang

고루틴과 동시성 프로그래밍

계단 2022. 5. 18. 09:49

Keyword


golang , goroutine

 

 

https://velog.io/@kineo2k/%EA%B3%A0%EB%A3%A8%ED%8B%B4%EC%9D%80-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81%EB%90%98%EB%8A%94%EA%B0%80

 

 

 

 

 

고루틴이란 ?


  • 고루틴(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 코어가 스레드를 변경할 때 발생하는데 고루틴을 이용하면 코어와 스레드는 변경되지 않고 오직 고루틴만 옮겨 다니기 때문입니다.
  • 코어가 스레드를 변경하지 않기 때문에 컨텍스트 스위칭 비용이 발생하지 않습니다.

 

 

 

Comments