공대생 정리노트

고루틴 본문

언어/Go

고루틴

woojinger 2021. 5. 3. 23:50

참고자료

medium.com/the-polyglot-programmer/what-are-goroutines-and-how-do-they-actually-work-f2a734f6f991

 

What are goroutines? And how do they actually work?

One of the main reasons that the Go Language has gained incredible popularity in the past few years is the simplicity it deals with…

medium.com

www.yes24.com/Product/Goods/24759320

 

디스커버리 Go 언어

실전에서 쓰는 Go 언어를 익히는 가장 확실한 방법Go는 범용 프로그래밍 언어로, 깔끔하고 간결하게 생산성 높은 프로그래밍이 가능하다. 작성한 코드를 빠르게 컴파일하고 가비지 컬렉션을 지

www.yes24.com

underflow101.tistory.com/17

 

[Golang] Go Routine (고 루틴) - Golang의 꽃 [심화]

이 론 어느 프로그래밍 언어나 해당 언어만의 강점, 혹은 "꽃"이라고 불릴 만한 것이 있다. 예를 들어 C언어의 꽃이라고 한다면 누가 물어도 "포인터"일 것이며, Python 같은 경우 "Life is too short, you

underflow101.tistory.com


1. 고루틴이란?

더보기

출처 : underflow101.tistory.com/17

  • 고루틴은 하나의 스레드 내에서 여러 고루틴으로 나뉠 수 있다. (1개의 스레드에 1+개의 고루틴이 있을 수 있음)
  • 고루틴 하나가 Wait() 등의 함수나 channel input에 의해 막힌다면(Block) 곧바로 다른 하나의 고루틴으로 스위치된다. 이는 같은 스레드에서 진행된다.
  • 이렇게 스위치하는 연산에는 고작 3개의 레지스터밖에 들어가지 않는다. 통째로 스레드를 스위치하는 것이 아니기 때문이다.
  • 그렇기에 고루틴은 일반적인 OS 스레드에 비해 굉장히 가볍고 빠를 수 있는 것이다.

여러 go routine이 하나의 OS Thread에서도 실행이 된다.

즉, Go routine은 OS Thread와 1대1 매핑이 되지 않고, Multiplexing으로 훨씬 적은 OS Thread를 사용한다.

Memory측면에서도 일반 OS Thread가 1MB의 stack을 갖는 반면, go routine은 이보다 훨씬 작은 몇 KB의 stack을 갖는다고 한다. 또한 이는 필요시 동적으로 증가한다.

Go Runtime은 go routine을 관리하면서 Go Channel을 통해 go routine간 통신을 쉽게 할 수 있도록 한다

출처 : https://medium.com/the-polyglot-programmer/what-are-goroutines-and-how-do-they-actually-work-f2a734f6f991 

Go Routine은 Go runtime의 virtual space에서만 존재하고, OS에서는 존재하지 않는다.

이로 인해 Go Runtime scheduler가 lifecycle을 관리하기 위해 필요하다.

Go Runtime은 세 개의 C struct를 유지한다.

  1. G struct : 한개의 goroutine과 그것의 stack과 status를 track하기 위한 field를 가진다.
  2. M struct : OS thread를 나타낸다. 또한 runnable goroutine을 가지고 있는 queue, 현재 실행중인 goroutine, cache, scheduler로의 reference 등을 가리키는 pointer를 가진다.
  3. Sched Struct : 한개의 global struct로 goroutine들을 담는 queue들을 track하고 M이나 Global Sched Lock과 같은 scheduler를 실행시킬 때 필요한 정보들을 갖고 있다.

M이 가지고 있는 runnable goroutine을 가진 Queue와 goroutine들의 free list를 담는 Queue 총 2개가 있다. 이 Queue들을 수정하려면 Global Sched Lock이 held가 되어야 한다.

 

만약 goroutine이 blocking call을 부르면(blocking system call 같은), runtime은 현재 thread에 있는 다른 goroutine들을 다른 runnable thread로 옮겨 block 되지 않는다.

그래서 blocking이 되지 않고 다른 goroutine으로 실행을 이어가려면 runtime에서 최소 1개 이상의 thread가 만들어져야 한다.

만약 communicate를 위해 channel을 사용한다면, Go에서 channel은 virtual space에서 존재하기 때문에 OS가 thread를 block하지 않는다. goroutine은 waiting state에 위치하고, 다른 실행 가능한 goroutine이 M struct로부터 스케줄된다.

Go Runtime Scheduler는 각각의 goroutine을 keep track하고, thread들의 pool에 스케줄한다.

Go Runtime Scheduler는 cooperative scheduling을 한다. 즉, 다른 goroutine은 오직 현재 goroutine이 blocking 되었거나, 완료가 되었을때만 schedule 될 수 있다. ex) blocking syscall, garbage collecion으로 인해 stop 되었을 때

 

2. 고루틴 사용

출처 : www.yes24.com/Product/Goods/24759320

sync.WaitGroup

var wg sync.WaitGroup
wg.Add(len(examples))
for _, ex := range examples {
	gp func(ex string){
    	defer wg.Done()
        
        ...
        
    }(ex)
}
wg.Wait()

wg에는 기본값 0이 맞춰져있다. 여기에 Add 함수를 사용하면 숫자를 더하게 된다.

wg.Done()은 호출될 때 마다 wg의 값을 1씩 차감한다.

카운터가 0이 되기 전까지 wg.Wait() 부분에서 멈춰있는다.

 

채널

<-chan // 받을 수만 있는 채널
chan<- // 보낼 수만 있는 채널
chan // 양방향 채널

예제

func Example(){
	ch := func() <-chan int{
    	c := make(chan int)
        go func(){
        	defer close(c)
            c <- 1
            c <- 2
            c <- 3
        }()
        return c
    }()
    for n := range ch{
    	fmt.Println(n)
    }
}

함수가 채널을 반환하는 패턴. 반환하는 함수는 단방향 채널을 반환해 받아가기만 하게 만듬

 

생성기 패턴

func Fibonacci(max int) <-chan int{
    c := make(chan int)
    go func() {
    	defer close(c)
        a, b := 0, 1
        for a <= max {
            c <- a
            a, b = b, a+b
        }
    }()
    return c
}

func ExampleFibonacci(){
	for fib := range Fibonacci(15){
    	fmt.Print(fib, ",")
    }
}

채널을 넘김으로써 상태 저장방법을 복잡하게 고민할 필요가 없고, 받는 쪽에서 for의 range를 이용 가능하다

 

버퍼 있는 채널

c := make(chan int, 10)

버퍼가 없는 채널에 값을 보낼 때는 받는 쪽도 준비가 되어 있어야 한다.

받는 쪽이 준비가 되어 있지 않아도 보내려면 버퍼를 잡아야 한다

만약 버퍼가 가득 차면 다른 고루틴이 채널에서 받아가지 않는 이상 고루틴이 채널을 보내는 곳에서 영원히 멈춘다

 

닫힌 채널

val, ok := <- c

채널이 열린 상태라면 ok에 true가, 닫혔다면 val에는 0, nil 등 기본값이, ok에는 false가 넘어온다.

'언어 > Go' 카테고리의 다른 글

Effective Go (1) 요약  (0) 2021.10.18
동시성 패턴  (0) 2021.05.05
Server 함수 정리  (0) 2020.12.29
Test시 Cannot import "main"이 뜨며 build fail  (0) 2020.12.20
Local package import하기 / Test 자동화  (0) 2020.12.19
Comments