Golang Concurrency - Gorutine (1)

2022. 8. 5. 22:38개발/Golang

728x90
반응형

오랜만에 블로그를 작성합니다..

사실 블로그를 안쓰며 살아볼려했지만.. 이렇게 글을쓰며 공부하는게 엄청나게 도움이 된다는 사실을 근래에 깨닫게 되고나서부터.. 시간이 걸리더라도 블로그에 글을 남기며 공부를 해볼려고 합니다.
처음주제는 Gorutine을 공부한 것들을 정리해볼려고 합니다.

 

고루틴을 알기전에 기본적인 프로세스와 스레드 등을 알아야 하는데요..

 

1. Process, Thread and Gorutine ??

- 프로세스명령과 사용자 데이터, 시스템 영역, 실행 과정에 수집한 다른 종류의 리소스로 구성된 실행 단위 라면..

- 스레드란 프로세스 보다 좀더 가벼운 작은 실행 단위입니다. 스레드는 프로세스에서 생성되며 제어 흐름과 스택을 따로 갖습니다. 

※ 간단히 말해 프로세스는 바이너리 파일을 실행한 것, 스레드는 프로세스의 일부분 

 

그렇다면 고루틴은 무엇일까요??

 

고루틴은 Go프로그램에서 동시에 실행할 수 있는 최소 단위입니다.

여기서 중요한 것은 최소단위 이며, 고루틴은 굉장히 가볍고 한 머신에서 수천 내지 수만 개를 거뜬히 구동할 수있는 장점이 있습니다.

 

실전에서 고루틴은 어떻게 수행이 될까요??

 

프로세스 하나가 여러 스레드로 구성되며, 다시 각 스레드는 여러 고루틴으로 구성됩니다. 단, 고루틴은 자신이 속할 프로세스 환경이 있어야 합니다. 그렇기에 고루틴을 생성하려면 스레드가 최소한 하나 있는 프로세스가 필요합니다.

 

1-1. Go 스케쥴러

커널 스케쥴러가 프로그램에 있는 스레드의 실행을 담당하듯이 Go Runtime에도 고루틴 실행을 담당하는 스케쥴러가 있습니다. Go 스케쥴러는 m:n 스케쥴링이라는 기법을 사용합니다. 여기서 m은 실행 되는 고루틴의 개수이고, n은 고루틴을 멀티 플렉싱할 OS의 스레드 개수입니다.

 

Go스케쥴러는 Go 프로그램에 있는 고루틴의 실행방법과 순서를 결정합니다.

( Go프로그램 모든 요소는 고루틴 형태로 실행!!! )

 

 

1-2.  동시성과 병렬성

동시성( Concurrency ) 과 병렬성 ( parallelism )을 같은 개념이 아닙니다.

동시성( Concurrency ) 은 최대한 독립적으로 실행 할 수 있게 구성하는 방식이라면 병렬성 ( parallelism )은 동시에 실행 되는것을 말한다. 

 

그렇다면 동시성을 써야하는 이유는 ?

 

동시성 설계가 제대로 된 시스템이라면 동시성을 지원하는 개체가 추가되면 병렬로 실행할 수 있는 일이 늘어 나기때문에 시스템 처리속도가 빨라지기 때문입니다.

따라서 문제를 동시성 관점에서 잘 표현하고 구현해야 병렬성의 원래 목표를 달성할 수 있기 때문입니다.

그러므로 개발자는 병렬성을 고민할 게 아니라 원래 풀려던 문제를 효율적으로 해결 할 수 있도록 시스템을 최대한 독립적인 구성 요소로 나눌 방법을 고민해야합니다.

 

 

2. Gorutine 생성방법

고루틴을 사용하기 위해서는 앞에 go라고 붙여주기만하면 끝이다.

 

package main

import (
	"fmt"
	"time"
)

func function() {
	for i := 0; i < 10; i++ {
		fmt.Print(i)
	}
	fmt.Println()
}

func main() {
	go function()

	go func() {
		for i := 0; i < 20; i++ {
			fmt.Print(i, " ")
		}
	}()
	time.Sleep(1 * time.Second)
}

< 결과 >

# go run main.go
0123456789
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

# go run main.go
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 0123456789

# go run main.go
0 1 2 3 4 5 6 0123456789 7 8 9 10 11 12 13 14 15 16 17 18 19

세번 실행 결과는 달랐다. 고루틴은 신경 쓰지 않으면 실행 순서를 제어 할 수 없다.  순서를 맞추고 싶으면 별도의 코드를 작성해야 합니다.  

 

3. Gorutine 마칠 때까지 기다리기

고루틴을 사용시 항상 함께하는 package가 있는데 바로 sync이다. 그중 sync.WaitGroup은 모든 고루틴이 종료될 때까지 대기해야 할 때 사용한다. 다음은 sync.WaitGroup이 제공하는 메서드다.

 func (wg *WaitGroup) Add(delta int): WaitGroup에 대기 중인 고루틴 개수 추가

 func (wg *WaitGroup) Done(): 대기 중인 고루틴의 수행이 종료되는 것을 알려줌

 func (wg *WaitGroup) Wait(): 모든 고루틴이 종료될 때까지 대기

출저 : https://thebook.io/006806/ch05/03/04/

 

package main

import (
	"flag"
	"fmt"
	"sync"
)

func main() {
	n := flag.Int("n", 20, "고루틴을 위한 숫자를 넣어주세요")
	flag.Parse()
	count := *n
	fmt.Printf("Going to create %d goroutines. \n", count)

	var waitGroup sync.WaitGroup

	fmt.Printf("%#v\n", waitGroup)
	for i := 0; i < count; i++ {
		waitGroup.Add(1)
		go func(x int) {
			defer waitGroup.Done()
			fmt.Printf("%d ", x)
		}(i)
	}

	fmt.Printf("%#v\n", waitGroup)
	waitGroup.Wait()
	fmt.Println("\nExiting...")

}

주의 할점은 sync.add(1)를 호출할려면 go루틴이 나오기전 즉 go문이 나오기전에 호출을 해야한다는 것이다.. 그래야 경쟁상태가 발생하는 것을 피할 수 있다. 그리고 작업을 마칠때 마다 sync.Done()함수를 작성하여 마무리를 해줘야한다. 이 함수를 통해 sync.WaitGroup변수의 카운터 값을 감소 시킨다. sync.Wait()를 호출하면 카운터가 0일때 실행을 멈춘다.

 

< 결과 >

 

# go run main.go  -n 20
Going to create 20 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[3]uint32{0x0, 0x0, 0x0}}
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[3]uint32{0x0, 0x14, 0x0}}
0 9 1 2 3 4 5 6 7 8 14 10 11 12 13 16 15 17 18 19


# go run main.go  -n 30
Going to create 30 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[3]uint32{0x0, 0x0, 0x0}}
0 3 20 2 4 5 8 7 9 6 11 10 13 14 15 12 16 17 18 1 24 sync.WaitGroup{noCopy:sync.noCopy{}, state1:[3]uint32{0x0, 0x1d, 0x0}}
29 25 26 27 28 22 19 23 21
Exiting...


# go run main.go  -n 30
Going to create 30 goroutines.
sync.WaitGroup{noCopy:sync.noCopy{}, state1:[3]uint32{0x0, 0x0, 0x0}}
0 6 1 3 4 9 7 8 5 11 10 12 13 2 14 15 18 16 17 19 20 21 22 23 24 sync.WaitGroup{noCopy:sync.noCopy{}, state1:[3]uint32{0x0, 0x1c, 0x0}}
29 25 26 27 28
Exiting...
 

실행 순서가 제각각이다.. 특히 생성한 고루틴이 많아질 수록 심해진다.. 

 

Mastring Go 책을 보고 정리한내용입니다..

 

728x90
반응형