티스토리 뷰

Let it GO/Go 동시성

Let it GO. Go의 동시성

iamgopher 2019. 11. 6. 23:54

요즘 개발자라면 한 마리 정도 키워본다는 귀요미 쥐가 있다.

이름은 Gopher. Google에서 발표한 프로그래밍 언어 Go의 상징이다.

 

현재 현업에서 사용하고 있으며 매우 만족도가 높아 이를 널리 퍼뜨리고자

Let it GO 시리즈를 시작해보려한다. (노래제목같다고? 이미 패러디도 있다. Write in Go: https://youtu.be/LJvEIjRBSDA )

go/goalng official repository에 있는 그림

 

자 그럼 시작해보자. 

 

이 Go라는 언어는 Stackoverflow survey에서 3년 연속 "Most Wanted Languages" 부문 Top 3에 들어간 대단히 핫한 언어 중 하나인데,

 

뭐가 좋길래 이렇게 핫할까? 

 

멀리 갈 것 없이 위키피디아에 Go를 검색해보면 바로 첫 문단에 등장한다.

위키피디아의 Go (프로그래밍 언어)

3가지로 요약이 되는데, 

1. GC (Garbage Collection) 를 지원한다.

2. Concurrent를 잘 지원한다.

3. 컴파일 언어이다.

4. Google이 개발한다

 

이 중에서 GC 그리고 동시성에 관해서 아는 바 글을 써보려한다.

 

사실 대부분의 주류 언어들은 동시성을 지원하는 주류 라이브러리들을 가지고 있고, 이러한 프로그래밍을 하는데 큰 지장은 없다.

그럼에도 불구하고 Go가 두각을 나타내는 이유는 바로 동시성을 잘 지원한다는 이유 때문이다.

 

이러한 특징은 언어설계부터 고려된 것으로 Go의 아버지인 Rob Pike가 쓴 글을 보면 다음과 같이 나온다.

Go is a compiled, concurrent, garbage-collected, statically typed language developed at Google.

(...)

Concurrency is important to the modern computing environment with its multicore machines running web servers with multiple clients, what might be called the typical Google program. This kind of software is not especially well served by C++ or Java, which lack sufficient concurrency support at the language level.

(ref. https://talks.golang.org/2012/splash.article)

 

기존 언어들이 라이브러리를 통해 동시성을 지원했다면, Go는 language level에서 지원한다는 큰 차이점이 존재한다.

 

즉 Go는 채널(Channel), 고루틴(Goroutine)과 같은 concurrent block들을 지원하고 이는 CSP(Communicating Sequential Processes)를 구현한 것이다. Go가 여러 동시성 모델 중에 CSP를 택한 이유는 Rob Pike (앞서 나온 Go의 아버지)가 기존에 Newsqueak란 CSP-based 언어를 작업했기 때문이다.

 

고루틴과 채널을 두 스푼 정도 맛보자.

Goroutines (고루틴)

모든 Go 프로그램은 Go의 런타임 위에서 실행된다. 쉽게 말하면, Go런타임이 시작되고 이것이 프로그램의 main 함수를 호출하게 된다.

그럼 고 루틴은 뭘까? Go 런타임이 직접 관리하는 스레드이다.

흔히 말하는 OS스레드와 정확히 일치하지는 않는다. 논리적으로 고루틴은 하나의 스레드처럼 동작하지만, 실제로는 여러 OS스레드를 이동해가며 스케줄링이 될 수 있다. 자세한 동작은 추후에 깊게 살펴보도록 하자.

 

다음은 list를 정렬하는 고 루틴을 시작시키는 코드이다.

함수 호출 앞에 go 키워드를 붙여, 고 루틴으로 시작시킨다. 단 1 단어로 여태껏 강조한 Go의 동시성을 편하게 누릴 수 있다.

go list.Sort()  // run list.Sort concurrently; don't wait for it.

 

기존의 흐름과는 독립적으로 list.Sort()는 새로운 고루틴 위에서 시작되는 것이다. 아무도 이 고 루틴을 기다리지도 않고, 종료되더라도 모른다.

보통 shell에서 & 를 붙이는 것과 비슷하게 생각할 수 있다. (실행시킨 shell과 동시에 실행되고, shell을 종료하면 기다리지 않고 종료되어버리는 것)

 

Channels (채널)

Go 채널(chan)은 단어 그대로 데이터를 주고받을 수 있는 통로 타입이다. Go의 철학 CSP를 구현한 것이다.

주목할 만한 것은 Go채널이 바로 타입, 즉 first-class로 정의되었다는 것이다. 채널을 함수의 인자 또는 반환 값으로 사용해도 되고, 채널들의 slice 또는 채널의 채널, 채널의 채널의 채널 등이 모두 가능하다는 것인데, 이는 채널의 사용성을 크게 늘려준다.

그리고 채널이 단순히 데이터 통로에 그치지 않고 Go 동시성의 축이 되는 이유는 채널이 동기/비동기를 모두 지원하기 때문이다

 

채널의 문법은 대단히 직관적이다.

보낼 때는 channel <- data

받을 때는 data <- channel

 

다음 예제가 이를 잘 설명한다.

c := make(chan int)  // Allocate a channel.
// Start the sort in a goroutine; when it completes, signal on the channel.
go func() {
    list.Sort()
    c <- 1  // Send a signal; value does not matter.
}()
doSomethingForAWhile()
<-c   // Wait for sort to finish; discard sent value.

 

1. 채널 c를 생성한다

2. 새로운 고루틴을 시작한다.

3-1. 새 고루틴에서는 list.Sort()를 하고

3-2. 기존 흐름에선 doSomethingForAWhile()을 한다

** 여기서 두 작업 중에 어느 것이 먼저 종료될지는 알기 어렵다.

 

3-1 이 먼저 종료되면 c <-1 이 실행되고, 채널 c에 데이터를 보내게 된다.

그러면 <-c에서 데이터를 받을 수 있어 프로그램이 종료가 되겠지만...

 

3-2가 먼저 종료되었다면? 아직 list.Sort()가 종료되지 않았으므로 이를 기다리고 싶다.

그리고 <-c는 아직 채널 c에 데이터가 없으므로, 받을 데이터가 없으니 block 되고 있게 된다.

결국 3-1이 종료되고 c <- 1으로 c에 데이터가 들어와야 이것을 받고 종료된다.

 

여기까지가 goroutine, channel의 맛보기였다.

 

다음 글에선 Go 동시성 문법을 알아보려 한다. 이러한 동시성 블록들이 어떻게 조립되고 그 내부는 어떤지 알기 이전에 문법을 살펴보고 가려한다.

'Let it GO > Go 동시성' 카테고리의 다른 글

Let it GO. Go의 동시성 - Goroutines (1)  (1) 2019.11.10
Let it GO. Go의 동시성 - 문법  (0) 2019.11.07
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함