Go 언어 의 꽃이라고 할 수 있는 동시성에 대해 공부해보고자 한다.
우선 1장에서는 용어에 대한 설명 과 정의를 하는데 보다 명확하게 이해하고 넘어가고자 작성한다.
1. 동시성 은 왜 컴퓨터 과학에서 중요한가?
-GUI에 기반한 프로그램은 사용자의 버튼 클릭 와 해당 버튼의 동작으로 이루어진다. 이러한 프로그램 파이프라인은 순차적으로 실행될 수밖에 없는 사용자와의 상호작용에 의해 성능이 제한된다. 다시 말해 몇 개의 코어를 가지던, 상관없이 사용자가 얼마나 빠르게 클릭하느냐 에 따라 이 프로그램의 성능이 결정된다는 의미이다.
- 스피곳 알고리즘 (원주율의 수수점 의 각자릿수에 대해 병렬로 처리하는 것)을 과잉병렬이라고 한다. 이를 해결하기 위해 프로그램에 서 더 많은 코어를 사용할 수 있게 만든다면 보다 성능향상 이 가능하다 반면 이에 각 코어 간의 결과를 어떻게 결합하고 저장하는가에 대한 새로운 문제점 이 발생한다. 이에 암달법칙 (병렬로 작업할 수 있는 부분이 많을수록 효율적으로 문제를 해결하는 것) 이 병렬처리가 시스템 성능 문제를 해결하는데 도움을 주는지에 대해 파악할 수 있다.
이런 병렬처리를 쉽게 하기 위해서는 프로그램, 애플리케이션을 수평적으로 작성할 것을 권장한다.
- 클라우딩 컴퓨팅에 의해 배포 및 수평확장에 새로운 패러다임이 등장하게 된다. 비교적 저렴한 비용으로 대교모 문제를 해결할 수 있는 컴퓨팅 성능에 접근하게 되었다. 그러나 이러한 자원들을 프로비저닝 하고, 인스턴스 간의 통신, 결과집계 저장 등 의 문제를 동시적으로 해결하는데 상당한 어려움이 있었으며 상황을 악화시키는 결과도 초래하곤 했다.
- 웹스케일(클라우드 회사에서 서버, 스토리지, 네트워크, 보안 등 구성요소를 최적화해서 운용하는 것) 이 등장하며, 과잉병렬을 가능하게 해 주었다
무어의 법칙이 깨짐 에 따라 , 하드웨의 기술발전이 이전만큼 유지되지 않고 있고 소프트웨어의 성능향상에 의존하기 어려워지고 있는 지금 동시성(멀티프로세스) 통한 성능향상을 추구하는 경향이 점점 더 강해지고 있고, 무어의 법칙의 깨짐에 대한 대안적인 접근 방법이 되어, 현 컴퓨터 과학에서 매우 중요한 개념으로 인식되고 있다.
2. 동시성 이 어려운 이유
- 레이스컨디션 : 하나의 동시작업이 어떤 변수를 읽으려고 시도하는 동안 또 다른 동시작업이 특정할 수 없는 시점에 동일변수에 값을 쓰려고 하는 데이터 레이스, 동시성 버그 중 가장 은밀한 유형 중 하나.
- 원자성 : 동작하는 콘텍스트 내에서 나누어지거나 중단되지 않는 것을 의미한다. 다시 말해 동시 실행되는 컨택스트 내에서 원자적이라 하면 안전하다는 것을 의미한다.
- 메모리접근 동기화: 각 프로그램 언어에서는 임계영역(공유리소스에 독점적 접근을 해야 하는 영역)의 동기화하는 다양한 방법을 제공한다.
-데드락 : 두 개의 작업이 서로의 작업이 끝나기만을 기다리는 상태
-라이브락: 동시에 연산을 수행하고 있지만, 이 연산들이 실제 프로그램의 상태를 진행하는데 아무런 영향을 주지 못하는 상태
-기아상태: 실행하는데 필요한 모든 리소스를 얻을 수 없는 상태
Go에서는 동시성의 문제를 돕기 위해 다양한 기본요소들은, 동시성 알고리즘을 보다 안전하고 명확하게 표현할 수 있다.
func race() {
var memoryAccess sync.Mutex
var data int
go func() {
memoryAccess.Lock()
data++
memoryAccess.Unlock()
fmt.Printf("in the go routine the value is %v.\n", data)
}()
memoryAccess.Lock()
if data == 0 {
fmt.Printf("the value is %v.\n", data)
} else {
fmt.Printf("the value is %v.\n", data)
}
memoryAccess.Unlock()
}
type value struct {
mu sync.Mutex
value int
}
func deadLock() {
var wg sync.WaitGroup
printSum := func(v1, v2 *value) {
defer wg.Done()
v1.mu.Lock()
defer v1.mu.Unlock()
time.Sleep(2 * time.Second)
v2.mu.Lock()
defer v2.mu.Unlock()
fmt.Printf("sum=%v\n", v1.value+v2.value)
}
var a, b value
wg.Add(2)
go printSum(&a, &b)
go printSum(&b, &a)
wg.Wait()
}
func liveLock() {
cadence := sync.NewCond(&sync.Mutex{})
go func() {
for range time.Tick(1 * time.Millisecond) {
cadence.Broadcast()
}
}()
takeStep := func() {
cadence.L.Lock()
cadence.Wait()
cadence.L.Unlock()
}
tryDir := func(dirName string, dir *int32, out *bytes.Buffer) bool {
fmt.Fprintf(out, " %v", dirName)
atomic.AddInt32(dir, 1)
takeStep()
if atomic.LoadInt32(dir) == 1 {
fmt.Fprintf(out, ". Success!")
return true
}
takeStep()
atomic.AddInt32(dir, -1)
return false
}
var left, right int32
tryLeft := func(out *bytes.Buffer) bool { return tryDir("left", &left, out) }
tryRight := func(out *bytes.Buffer) bool { return tryDir("right", &right, out) }
walk := func(walking *sync.WaitGroup, name string) {
var out bytes.Buffer
defer func() { fmt.Println(out.String()) }()
defer walking.Done()
fmt.Fprintf(&out, "%v is trying to scoot:", name)
for i := 0; i < 5; i++ {
if tryLeft(&out) || tryRight(&out) {
return
}
}
fmt.Fprintf(&out, "\n%v tosses her hands up in exasperation!", name)
}
var peopleInHallway sync.WaitGroup
peopleInHallway.Add(2)
go walk(&peopleInHallway, "Alice")
go walk(&peopleInHallway, "Barbara")
peopleInHallway.Wait()
}
func starvation() {
var wg sync.WaitGroup
var sharedLock sync.Mutex
const runtime = 1 * time.Second
greedyWorkder := func() {
defer wg.Done()
var count int
for begin := time.Now(); time.Since(begin) <= runtime; {
sharedLock.Lock()
time.Sleep(3 * time.Nanosecond)
sharedLock.Unlock()
count++
}
fmt.Printf("Greedy worker was able to execute %v work loops.\n", count)
}
polliteWorker := func() {
defer wg.Done()
var count int
for begin := time.Now(); time.Since(begin) <= runtime; {
sharedLock.Lock()
time.Sleep(1 * time.Nanosecond)
sharedLock.Unlock()
sharedLock.Lock()
time.Sleep(1 * time.Nanosecond)
sharedLock.Unlock()
sharedLock.Lock()
time.Sleep(1 * time.Nanosecond)
sharedLock.Unlock()
count++
}
fmt.Printf("Polite worker was able to execute %v work loops.\n", count)
}
wg.Add(2)
go greedyWorkder()
go polliteWorker()
wg.Wait()
}
'Go > 고루틴' 카테고리의 다른 글
[Concurrency in Go] 4장 Go의 동시성 패턴 -2 (2) | 2023.06.12 |
---|---|
[Concurrency in Go] 4장 Go의 동시성 패턴 (0) | 2023.06.10 |
[Concurrency in Go] 3장 Go의 동시성 구성요소-2 (0) | 2023.06.08 |
[Concurrency in Go] 3장 Go의 동시성 구성요소 (3) | 2023.06.06 |
[Concurrency in Go] 2장 코드모델링 (1) | 2023.05.31 |