
๋ค์ด๊ฐ๋ฉฐ
๊ณ ๋ฃจํด์ Go์ ๋น๋๊ธฐ ๋ฉ์ปค๋์ฆ์ ๋๋ค. Go์ ์ต๋ ๋งค๋ ฅํฌ์ธํธ๋ผ๊ณ ๋ด๋ ๊ณผ์ธ์ด ์๋์ฃ . ๊ณ ๋ฃจํด์ ์ฌ์ฉํ๋ฉด ์ด๋ฒคํธ ์ฒ๋ฆฌ, ๋ณ๋ ฌ ํ๋ก๊ทธ๋๋ฐ์ด ๊ฐ๋จํด์ง๋๋ค. Go๋ ๋น๋๊ธฐ ์ธก๋ฉด์์ ๋ค๋ฅธ ์ธ์ด์ ๋ค๋ฅด๊ฒ CSP(Communicating Sequential Processes)๋ผ๋ ํ์ ์ ๋ชจ๋ธ์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ๊ทธ๋์ ๊ณ ๋ฃจํด๊ณผ ์ฑ๋๋ฐฉ์์ด ํ์ํ๊ฒ ๋์ต๋๋ค. ๋ํ ๊ณ ๋ฃจํด์ผ๋ก ์คํํ๋ ์ค๋ ๋ ๋จ์๊ฐ ๊ฒฝ๋(2KB)์ด๊ธฐ ๋๋ฌธ์ 10๋ง๊ฐ์ task๋ ๊ณ ๋ฃจํด์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค(์ผ๋ฐ์ ์ธ OS ์ค๋ ๋๋ 1MB ์ฉ๋).
๊ณ ๋ฃจํด ์ค์ผ์ค๋ฌ

Go์ ์ค์ผ์ค๋ฌ๋ ์ ์ ๋ ๋ฒจ ์ค๋ ๋ ์ค์ผ์ค๋ฌ(user-space scheduler) ์ ๋๋ค. ์ฆ, OS๊ฐ ์๋ Go ๋ฐํ์์ด ์ง์ goroutine์ OS thread ์์์ ์ค์ผ์ค๋งํฉ๋๋ค. ํต์ฌ ๋ชฉํ๋ ์์ญ๋ง ๊ฐ์ ๋์ ์คํ ๋จ์๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๊ฒ์ ๋๋ค. Go ๋ฐํ์์ 3๊ฐ์ง ์ฃผ์ ์ํฐํฐ(GMP)๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
| ์์ | ์ค๋ช | ๋น์ |
| G (Goroutine) | ์คํ ๋จ์ (ํจ์ + ์คํ + ์ํ) | ์์ (Task) |
| M (Machine) | ์ค์ OS Thread | ๊ทผ๋ก์(Thread) |
| P (Processor) | ์คํ ๊ฐ๋ฅํ context (run queue ํฌํจ) | ์์ ๋(Execution Slot) |
G๋ ๋ ๋ฆฝ์ ์ธ ์คํ ๊ฒฝ๋ก์ ์คํ์ ๊ฐ์ง๋ฉฐ M์ ์ํด ์คํ๋ฉ๋๋ค. M์ ์ฐ๋ฆฌ๊ฐ ์๋ OS thread์ ๋๋ค. P๋ ๋ก์ปฌ ์ค์ผ์ค๋ฌ ์ญํ ์ ํ๋ ๊ตฌ์กฐ์ฒด๋ก, ์คํํ ๊ณ ๋ฃจํด ๋ชฉ๋ก์ ๊ด๋ฆฌํ๊ณ M์๊ฒ ๊ณ ๋ฃจํด์ ์ ๊ณตํฉ๋๋ค. ์์คํ ์ ๊ฐ CPU์ฝ์ด์๋ ํ๋์ P๊ฐ ํ ๋น๋ฉ๋๋ค.
์ค์ผ์ค๋ง ํ๋ก์ธ์ค
+-----------------------------+
| Go Runtime |
+-----------------------------+
| P1 → runq: G1, G2, G3 |
| P2 → runq: G4, G5 |
| M1 <-> P1 |
| M2 <-> P2 |
| Global Run Queue |
+-----------------------------+
๊ธฐ๋ณธ ํ๋ฆ:
- OS Thread(M)๋ P๋ฅผ ์ ์ ํด์ผ๋ง goroutine(G)์ ์คํํ ์ ์์ต๋๋ค.
- ๊ฐ P๋ ์์ฒด run queue๋ฅผ ๊ฐ์ง๊ณ ์์ด G๋ฅผ ๋ก์ปฌ์์ ์ฐ์ ์คํํฉ๋๋ค.
- ๋ก์ปฌ queue๊ฐ ๋น๋ฉด, ๋ค๋ฅธ P์ queue์์ work stealing์ ์ํํฉ๋๋ค.
- ์คํ ์ค blocking syscall(I/O ๋ฑ)์ด ๋ฐ์ํ๋ฉด:
- ํด๋น M์ block๋จ.
- Go runtime์ ์๋ก์ด M์ ์์ฑํด ๋จ์ G๋ค์ ๊ณ์ ์คํํฉ๋๋ค.
→ ์ฆ, Blocking I/O๊ฐ ์ ์ฒด ์คํ์ ๋ง์ง ์์ต๋๋ค.
์ฉ์ด ์ค๋ช
- Run Queue: ๊ฐ P๋ ์คํํ ๊ณ ๋ฃจํด ๋ชฉ๋ก์ ๊ฐ์ง ๋ก์ปฌ run queue๋ฅผ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ M์ด queue์์ ๊ณ ๋ฃจํด์ ๊ฐ์ ธ์ ์คํํฉ๋๋ค.
- Work Stealing: P์ ๋ก์ปฌ run queue๊ฐ ๋น์ด ์์ผ๋ฉด, ๋ค๋ฅธ P์ run queue์์ ๊ณ ๋ฃจํด์ ‘ํ์ณ’์ฌ ์ ์์ต๋๋ค. ์ด๋ฅผ ํตํด ๋ถํ๋ฅผ ๊ท ๋ฑํ๊ฒ ๋ถ์ฐ์ํค๊ณ CPU ์ฌ์ฉ๋ฅ ์ ์ต์ ํํฉ๋๋ค.
- Syscall and Blocking: ๊ณ ๋ฃจํด์ด ์์คํ ํธ์ถ์ ํ๊ฑฐ๋ ๋ธ๋กํน ์ฐ์ฐ์ ์ํํ๋ฉด, ํด๋น ๊ณ ๋ฃจํด์ ๋ธ๋ฝ ์ํ๊ฐ ๋๊ณ ๋ค๋ฅธ ๊ณ ๋ฃจํด์ด ์คํ๋ฉ๋๋ค.
- Global Run Queue: ์์คํ ์ ์ฒด์์ ์ฌ์ฉํ ์ ์๋ ๊ธ๋ก๋ฒ run queue๋ ์กด์ฌํฉ๋๋ค. ์๋ก ์์ฑ๋ ๊ณ ๋ฃจํด์ ์ด ๊ธ๋ก๋ฒ run queue์ ์ถ๊ฐ๋๋ฉฐ, P๋ ํ์์ ๋ฐ๋ผ ์ด๊ณณ์์ ๊ณ ๋ฃจํด์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
- Spinning :P๊ฐ ๊ณ ๋ฃจํด์ ์คํํ ์ค๋น๊ฐ ๋์์ง๋ง ๋ก์ปฌ run queue๊ฐ ๋น์ด์์ ๋, P๋ ๋ค๋ฅธ P์ run queue์์ ์์ ์ ํ์น๋ ค๊ณ ์๋ํ ์ ์์ต๋๋ค.

๋ณ๋ ฌ์ฑ(Parallelism)๊ณผ ๋์์ฑ(Concurrency)
- ๋์์ฑ(Concurrency): ์ฌ๋ฌ goroutine์ด ์งํ ์ค์ธ ์ํ๋ฅผ ์ ์ง.
- ๋ณ๋ ฌ์ฑ(Parallelism): ์ค์ ๋ก ์ฌ๋ฌ CPU์์ ๋์์ ์คํ.
Go์ ์ค์ผ์ค๋ฌ๋ Concurrency๋ฅผ ๋ณด์ฅํ์ง๋ง,
๋ณ๋ ฌ ์คํ ์๋ GOMAXPROCS ํ๊ฒฝ ๋ณ์์ ์ํด ์ ํ๋ฉ๋๋ค.
runtime.GOMAXPROCS(4)
→ ์ต๋ 4๊ฐ์ P๊ฐ ๋์์ goroutine์ ์คํํ ์ ์์ต๋๋ค(์ฆ, 4๊ฐ ์ฝ์ด ์ฌ์ฉ).
Go routine ์ฝ๋
package main
import "fmt"
func hello() {
fmt.Println("Hello, world!")
}
func main() {
go hello() // ํจ์๋ฅผ ๊ณ ๋ฃจํด์ผ๋ก ์คํ
fmt.Scanln() // main ํจ์๊ฐ ์ข
๋ฃ๋์ง ์๋๋ก ๋๊ธฐ
}
ํจ์๋ฅผ ํธ์ถํ ๋ ์์ go ํค์๋๋ฅผ ๋ถ์ด๋ฉด ํด๋น ํจ์๋ ๊ณ ๋ฃจํด์ผ๋ก ์คํ๋ฉ๋๋ค. ์ฌ๊ธฐ์๋ hello ํจ์๋ฅผ ๊ณ ๋ฃจํด์ผ๋ก ์คํํฉ๋๋ค. ๊ณ ๋ฃจํด์ผ๋ก hello ํจ์๋ฅผ ์คํํ๋ฉด main ํจ์์ ๋์์ ์คํ๋๊ธฐ ๋๋ฌธ์ hello ํจ์ ์์ fmt.Println์ด ํธ์ถ๋๊ธฐ ์ ์ main ํจ์๊ฐ ์ข ๋ฃ๋์ด๋ฒ๋ฆฝ๋๋ค. ๋ฐ๋ผ์ fmt.Scanln์ ์ฌ์ฉํ์ฌ main ํจ์๊ฐ ์ข ๋ฃ๋์ง ์๋๋ก ๋๊ธฐํฉ๋๋ค(์ํฐ๋ฅผ ์ ๋ ฅํ๋ฉด ๋๊ธฐ๋ฅผ ๋๋ ๋๋ค).
๋ค์์ ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ์ฌ ๊ณ ๋ฃจํด์ 100๊ฐ ์์ฑํฉ๋๋ค.
package main
import (
"fmt"
"math/rand"
"time"
)
func hello(n int) {
r := rand.Intn(100) // ๋๋คํ ์ซ์ ์์ฑ
time.Sleep(time.Duration(r)) // ๋๋คํ ์๊ฐ ๋์ ๋๊ธฐ
fmt.Println(n) // n ์ถ๋ ฅ
}
func main() {
for i := 0; i < 100; i++ { // 100๋ฒ ๋ฐ๋ณตํ์ฌ
go hello(i) // ๊ณ ๋ฃจํด 100๊ฐ ์์ฑ
}
fmt.Scanln()
}
๊ณ ๋ฃจํด 100๊ฐ๋ฅผ ๋์์ ์คํํ๋ฉด์ ๊ฐ ๊ณ ๋ฃจํด์ ๋๋คํ ์๊ฐ๋์ ๋๊ธฐํฉ๋๋ค. rand.Intn ํจ์์ 100์ ๋ฃ์ผ๋ฉด 0๋ถํฐ 100๊น์ง ๋๋คํ ์ซ์๋ฅผ ๋ฆฌํดํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ time.Sleep ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ค์ ํ ์๊ฐ๋์ ๋๊ธฐํฉ๋๋ค. ์ฌ๊ธฐ์ time.Sleep ํจ์์ ๊ฐ์ ๋ฃ์ ๋๋ time.Duration ํ์ ์ผ๋ก ๋ณํํด์ค๋๋ค.
๋ฉํฐ์ฝ์ด ํ์ฉํ๊ธฐ
Go ์ธ์ด๋CPU ์ฝ์ด๋ฅผ ํ ๊ฐ๋ง ์ฌ์ฉํ๋๋ก ์ค์ ๋์ด ์์ต๋๋ค. ๋ค์์ ์์คํ ์ ๋ชจ๋ CPU ์ฝ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // CPU ๊ฐ์๋ฅผ ๊ตฌํ ๋ค ์ฌ์ฉํ ์ต๋ CPU ๊ฐ์ ์ค์
fmt.Println(runtime.GOMAXPROCS(0)) // ์ค์ ๊ฐ ์ถ๋ ฅ
s := "Hello, world!"
for i := 0; i < 100; i++ {
go func(n int) { // ์ต๋ช
ํจ์๋ฅผ ๊ณ ๋ฃจํด์ผ๋ก ์คํ
fmt.Println(s, n)
}(i)
}
fmt.Scanln()
}
runtime.NumCPU ํจ์๋ก ํ์ฌ ์์คํ ์ CPU ์ฝ์ด ๊ฐ์๋ฅผ ๊ตฌํ ๋ค runtime.GOMAXPROCS ํจ์์ ์ค์ ํด์ค๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ชจ๋ CPU ์ฝ์ด์์ ๊ณ ๋ฃจํด์ ์คํํ ์ ์์ต๋๋ค.
runtime.GOMAXPROCS ํจ์๋ CPU ์ฝ์ด ๊ฐ์๋ฅผ ๊ตฌํ์ง ์๊ณ , ํน์ ๊ฐ์ ์ค์ ํด๋ ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ runtime.GOMAXPROCS ํจ์์ 0์ ๋ฃ์ผ๋ฉด ์ค์ ๊ฐ์ ๋ฐ๊พธ์ง ์์ผ๋ฉฐ ํ์ฌ ์ค์ ๊ฐ๋ง ๋ฆฌํดํฉ๋๋ค.
ํด๋ก์ ๋ฅผ ๊ณ ๋ฃจํด์ผ๋ก ์คํํ๊ธฐ
ํจ์ ์์์ ํด๋ก์ ๋ฅผ ์ ์ํ ๋ค ๊ณ ๋ฃจํด์ผ๋ก ์คํํ ์ ์์ต๋๋ค. ์์ ์ ์ถ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ์ข ๋ ํ์คํ๊ฒ ํํํ๊ธฐ ์ํด CPU ์ฝ์ด๋ ํ๋๋ง ์ฌ์ฉํ๋๋ก ์ค์ ํฉ๋๋ค.
package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(1) // CPU๋ฅผ ํ๋๋ง ์ฌ์ฉ
s := "Hello, world!"
for i := 0; i < 100; i++ {
go func(n int) { // ์ต๋ช
ํจ์๋ฅผ ๊ณ ๋ฃจํด์ผ๋ก ์คํ(ํด๋ก์ )
fmt.Println(s, n) // s์ ๋งค๊ฐ๋ณ์๋ก ๋ฐ์ n ๊ฐ ์ถ๋ ฅ
}(i) // ๋ฐ๋ณต๋ฌธ์ ๋ณ์๋ ๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ์ค
}
fmt.Scanln()
}
์คํ ๊ฒฐ๊ณผ
Hello, world! 0
Hello, world! 1
Hello, world! 2
.. ์๋ต ..
Hello, world! 97
Hello, world! 98
Hello, world! 99
ํด๋ก์ ๋ฅผ ๊ณ ๋ฃจํด์ผ๋ก ์คํํ ๋ ๋ฐ๋ณต๋ฌธ ์์์ ๋ณ์ ์ฌ์ฉ์ ์ฃผ์ํด์ผ ํฉ๋๋ค. ์์ ์์๋ ๋ฐ๋ณต๋ฌธ์ผ๋ก ์ฆ๊ฐํ๋ i๋ฅผ ํด๋ก์ ์์ ๊ทธ๋๋ก ์ฌ์ฉํ์ง ์๊ณ , ๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ์ฃผ์์ต๋๋ค. ์ผ๋ฐ ํด๋ก์ ๋ ๋ฐ๋ณต๋ฌธ ์์์ ์์๋๋ก ์คํ๋์ง๋ง ๊ณ ๋ฃจํด์ผ๋ก ์คํํ ํด๋ก์ ๋ ๋ฐ๋ณต๋ฌธ์ด ๋๋ ๋ค์ ๊ณ ๋ฃจํด์ด ์คํ๋ฉ๋๋ค.
์ฝ๋๋ฅผ ์์ ํ์ฌ ๋ณ์ i๋ฅผ ํด๋ก์ ์ ๋งค๊ฐ๋ณ์๋ก ๋๊ธฐ์ง ์๊ณ fmt.Println์ผ๋ก ๋ฐ๋ก ์ถ๋ ฅํด๋ด ๋๋ค.
func main() {
runtime.GOMAXPROCS(1)
s := "Hello, world!"
for i := 0; i < 100; i++ {
go func() {
fmt.Println(s, i) // ๋ฐ๋ณต๋ฌธ์ ๋ณ์๋ฅผ ํด๋ก์ ์์ ๋ฐ๋ก ์ถ๋ ฅ
}()
}
fmt.Scanln()
}
์คํ ๊ฒฐ๊ณผ
Hello, world! 100
Hello, world! 100
Hello, world! 100
... ์๋ต ...
Hello, world! 100
Hello, world! 100
Hello, world! 100
์ด๋ ๊ฒ ๋๋ฉด ๋ณ์ i๋ 0๋ถํฐ 99๊น์ง ์ฆ๊ฐํ๊ณ , ๋ค์ 100์ด ๋๋ฉด์ ๋ฐ๋ณต๋ฌธ์ด ๋๋ฉ๋๋ค. ๊ณ ๋ฃจํด์ ๋ฐ๋ณต๋ฌธ์ด ์์ ํ ๋๋ ๋ค์์ ์์ฑ๋๋ฏ๋ก ๊ณ ๋ฃจํด์ด ์์ฑ๋ ์์ ์ ๋ณ์ i์ ๊ฐ์ 100์ ๋๋ค. ๋ฐ๋ผ์ ๋ชจ๋ Hello, world! 100์ด ์ถ๋ ฅ๋ฉ๋๋ค(CPU ์ฝ์ด๋ฅผ ํ๋๋ง ์ฌ์ฉํ์ ๋์ ์ํฉ์ ๋๋ค. CPU ์ฝ์ด๋ฅผ ์ฌ๋ฌ ๊ฐ ์ฌ์ฉํ๋ฉด ๊ฒฐ๊ณผ๋ ์กฐ๊ธ ๋ฌ๋ผ์ง์ง๋ง ์๋ํ๋๋๋ก 0๋ถํฐ 99๊น์ง ๋น ์ง์์ด ์ถ๋ ฅ๋์ง๋ ์์ต๋๋ค).
ํด๋ก์ ๋ฅผ ๊ณ ๋ฃจํด์ผ๋ก ์คํํ ๋ ๋ฐ๋ณต๋ฌธ์ ์ํด ๋ฐ๋๋ ๋ณ์๋ ๋ฐ๋์ ๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ์ค๋๋ค. ์ฆ ๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ์ฃผ๋ ์์ ์ ํด๋น ๋ณ์์ ๊ฐ์ด ๋ณต์ฌ๋๋ฏ๋ก ๊ณ ๋ฃจํด์ด ์์ฑ๋ ๋ ๊ทธ๋๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ํ, CPU ์ฝ์ด๋ฅผ ํ๋๋ง ์ฌ์ฉํ๋ ์ฌ๋ฌ ๊ฐ ์ฌ์ฉํ๋ ์๊ด์์ด ๋ฐ๋ณต๋ฌธ์ ์ํด ๋ฐ๋๋ ๋ณ์๋ ๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ์ฃผ์ด์ผ ํฉ๋๋ค.
์ถ๊ฐ ํด์ค
Go 1.4์์๋ runtime.GOMAXPROCS(1)๋ฅผ ์ค์ ํ๋ฉด ๊ณ ๋ฃจํด์ด ์์๋๋ก ์คํ๋์ง๋ง Go 1.5 ์ด์๋ถํฐ๋ ์์๋๋ก ์คํ๋์ง ์์ ์๋ ์์ต๋๋ค. ์ด๋ฒ ์์ ๋ ๊ณ ๋ฃจํด์ด ์์๋๋ก ์คํ๋๋์ง๋ฅผ ์์๋ณด๋ ๊ฒ์ด ์๋๋ผ ํด๋ก์ ๋ฅผ ๊ณ ๋ฃจํด์ ์คํํ์ ๋ ๋ณ์๊ฐ ์ ๋๋ก ์ ๋ฌ๋๋์ง๋ฅผ ์์๋ณด๋ ๊ฒ์ด ๋ชฉ์ ์
๋๋ค. Go ์ธ์ด์์ ๊ณ ๋ฃจํด์ ์คํ ์์๋ฅผ ๋ณด์ฅํ๋ ค๋ฉด ๋๊ธฐ ์ฑ๋ ๋ฑ์ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
์๋ธ ๊ณ ๋ฃจํด์ด ์ข ๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๊ธฐ
syncํจํค์ง์ WaitGroup๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ์ค๋ ๋ ์ข ๋ฃ๊น์ง ๊ธฐ๋ค๋ฆด ์ ์์ต๋๋ค.
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup // โถ 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, 1000000000)
}
wg.Wait() // โน ๋ชจ๋ ์์
์ด ์๋ฃ๋๊ธธ ๊ธฐ๋ค๋ฆผ.
fmt.Println("๋ชจ๋ ๊ณ์ฐ์ด ์๋ฃ๋์์ต๋๋ค.")
}
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
1๋ถํฐ 1000000000๊น์ง์ ํฉ๊ณ๋ 500000000500000000์
๋๋ค.
๋ชจ๋ ๊ณ์ฐ์ด ์๋ฃ๋์์ต๋๋ค.
WaitGroup ๊ฐ์ฒด์ Add()๋ก ์์
๊ฐ์๋ฅผ ์ค์ ํ๊ณ Done()์ ์์
์ด ์๋ฃ๋ ๋๋ง๋ค ํธ์ถํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ Wait() ๋ถ๋ถ์์ ๋ชจ๋ ์์
์ด ์๋ฃ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฝ๋๋ค.
Mutex Lock์ฌ์ฉ
Lock์ผ๋ก ๊ณต์ ์์์ ๋ณดํธํ๋ ์์ ์ ๋๋ค.
package main
import (
"fmt"
"sync"
"time"
)
var mutex sync.Mutex // โถ ํจํค์ง ์ ์ญ ๋ณ์ ๋ฎคํ
์ค
type Account struct {
Balance int
}
func DepositAndWithdraw(account *Account) {
mutex.Lock() // โท ๋ฎคํ
์ค ํ๋
defer mutex.Unlock() // โธ defer๋ฅผ ์ฌ์ฉํ Unlock()
if account.Balance < 0 {
panic(fmt.Sprintf("Balance should not be negative value: %d", account.Balance))
}
account.Balance += 1000
time.Sleep(time.Millisecond)
account.Balance -= 1000
}
func main() {
var wg sync.WaitGroup
account := &Account{0}
wg.Add(10)
for i := 0; i < 10; i++ {
go func() {
for {
DepositAndWithdraw(account)
}
wg.Done()
}()
}
wg.Wait()
}
Deposit ํจ์๋ฅผ ๋ณด๋ฉด mutex.Lock()์ผ๋ก ์์ ์ ๊ทผ ๊ถํ์ ํ๋ํ๊ณ ์ง์ฐ ํธ์ถ๋ก ํจ์๊ฐ ์ข
๋ฃ๋ ๋ Unlock()์ ์คํํฉ๋๋ค. ์ ์์ ์์ ๋ฎคํ์ค๊ฐ ์์ผ๋ฉด ํจ๋์ด ๋ฐ์ํฉ๋๋ค. Unlock()์ผ๋ก ํญ์ ์์ ์ ๊ทผ ๊ถํ์ ๋ฐ๋ฉํด์ผ ํ๋ค๋ ์ ์ ๊ธฐ์ตํฉ์๋ค.
๋ค์์ ๋ฐ๋๋ฝ์ด ๋ฐ์ํ๋ ์์ ์
๋๋ค.
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
rand.Seed(time.Now().UnixNano())
wg.Add(2)
fork := &sync.Mutex{} // โถ ํฌํฌ์ ์์ ๋ฎคํ
์ค
spoon := &sync.Mutex{}
go diningProblem("A", fork, spoon, "ํฌํฌ", "์์ ") // โท A๋ ํฌํฌ ๋จผ์
go diningProblem("B", spoon, fork, "์์ ", "ํฌํฌ") // โธ B๋ ์์ ๋จผ์
wg.Wait()
}
func diningProblem(name string, first, second *sync.Mutex, firstName, secondName string) {
for i := 0; i < 100; i++ {
fmt.Printf("%s ๋ฐฅ์ ๋จน์ผ๋ ค ํฉ๋๋ค.\\n", name)
first.Lock() // โน ์ฒซ ๋ฒ์งธ ๋ฎคํ
์ค๋ฅผ ํ๋ ์๋
fmt.Printf("%s %s ํ๋\\n", name, firstName)
second.Lock() // โ ๋ ๋ฒ์งธ ๋ฎคํ
์ค๋ฅผ ํ๋ ์๋
fmt.Printf("%s %s ํ๋\\n", name, secondName)
fmt.Printf("%s ๋ฐฅ์ ๋จน์ต๋๋ค\\n", name)
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
second.Unlock() // โ ๋ฎคํ
์ค ๋ฐ๋ฉ
first.Unlock()
}
wg.Done()
}
B ๋ฐฅ์ ๋จน์ผ๋ ค ํฉ๋๋ค.
B ์์ ํ๋
B ํฌํฌ ํ๋
B ๋ฐฅ์ ๋จน์ต๋๋ค
A ๋ฐฅ์ ๋จน์ผ๋ ค ํฉ๋๋ค.
A ํฌํฌ ํ๋
A ์์ ํ๋
A ๋ฐฅ์ ๋จน์ต๋๋ค
B ๋ฐฅ์ ๋จน์ผ๋ ค ํฉ๋๋ค.
A ๋ฐฅ์ ๋จน์ผ๋ ค ํฉ๋๋ค.
B ์์ ํ๋
A ํฌํฌ ํ๋
fatal error: all goroutines are asleep - deadlock!
...
exit status 2
(1) ํฌํฌ์ ์์ ๋ฎคํ ์ค ๋ ๊ฐ๋ฅผ ๋ง๋ ๋ค. (2) A๋ ํฌํฌ๋ฅผ ๋จผ์ ๋ค๊ณ (3) B๋ ์์ ๋ฅผ ๋จผ์ ๋ ๋ค.
ํจ์ ๋ด์์ (4) ์ฒซ ๋ฒ์งธ ๋ฎคํ์ค์ (5) ๋ ๋ฒ์งธ ๋ฎคํ์ค๋ฅผ ๋ชจ๋ ํ๋ํ ๊ฒฝ์ฐ์ ๋ฐฅ์ ๋จน๊ณ (6) ๋ ๋ฎคํ ์ค๋ฅผ ๋ฐ๋ฉํ๋ค.
์คํ๊ฒฐ๊ณผ๋ A,B ๋๋ค ๋ฐฅ์ ๋จน์ง ๋ชปํ๊ณ ๋ฌดํํ ๋๊ธฐํ๊ฒ ๋๊ณ Go ์ธ์ด์์๋ ๋ฐ๋๋ฝ์ ๊ฐ์งํ๊ณ ์๋ฌ๋ฅผ ๋ฐํํฉ๋๋ค. ๋ฐ๋๋ฝ ๋ฌธ์ ๋ ๋์์ฑ ํ๋ก๊ทธ๋๋ฐ์์ ํด๊ฒฐํ๊ธฐ ํ๋ ๋์ ์
๋๋ค. google cloud profiler์ ๊ฐ์ ํ๋กํ์ผ๋ง ํด์ ์ด์ฉํด์ ๋ฎคํ
์ค ๊ฒ์ฌ๋ฅผ ํ ์ ์์ต๋๋ค.
Select ์ฌ์ฉ
๋ค์์ select๋ฅผ ์ฌ์ฉํ์ฌ ๋ค์ค ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ์์ ์ ๋๋ค.
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "Job from ch1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "Job from ch2"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("Received:", msg1)
case msg2 := <-ch2:
fmt.Println("Received:", msg2)
}
}
}
select๋ ์ฌ๋ฌ ์ฑ๋ ์ค ์ค๋น๋ ์ฑ๋์ ๋น์ฐจ๋จ(non-blocking)์ผ๋ก ์ ํํฉ๋๋ค. ๋คํธ์ํฌ ์์ฒญ, ํ์์์ ์ฒ๋ฆฌ, ์ด๋ฒคํธ ๊ธฐ๋ฐ ๋ฃจํ ๋ฑ์ ํ์์ ์ ๋๋ค. ๋ค์์ ์๊ฐํ ์ฑ๋ํธ์์ ์ด์ด์ ์งํํ๊ฒ ์ต๋๋ค.
๋ง์น๋ฉฐ
๊ณ ๋ฃจํด์ ์ฌ์ฉํ๋ค๊ณ ํด์ ์ปจํ ์คํธ ์ค์์นญ์ด ์ผ์ด๋์ง ์๋ ๊ฒ์ ์๋๋๋ค. ํ์ง๋ง ํ๋ก์ธ์ค/์ค๋ ๋ ๋จ์์ ์ปจํ ์คํธ ์ค์์นญ๊ณผ ๊ทผ๋ณธ์ ์ผ๋ก ๋ค๋ฅธ ๋ถ๋ถ์ด ์์ต๋๋ค. ๋ฐ๋ก ์ปค๋ <-> ์ ์ ๋ชจ๋์ ์ ํ์ด ์ผ์ด๋์ง ์๋ ๋ค๋ ์ ์ ๋๋ค. OS๋จ ์์์ ๊ณ ๋ฃจํด๋ผ๋ฆฌ์ ์ค์์นญ์ด ์ผ์ด๋๊ธฐ ๋๋ฌธ์ ๋น์ฉ์ ํจ์ฌ ๋จ์ถํ ์ ์์ต๋๋ค. Go์ ์ค๊ณ ์ฒ ํ์ผ๋ก ๋ง๋ฌด๋ฆฌํ๊ฒ ์ต๋๋ค.
"Don’t communicate by sharing memory; share memory by communicating"
'ํ๋ก๊ทธ๋๋ฐ ์ธ์ด > Go' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Go] Map ์๋ฆฌ์ ํ์ฉ(feat. hash) (0) | 2025.11.05 |
|---|---|
| [Go] ์ฌ๋ผ์ด์ค(slice)์๋ ํฌ์ธํฐ๊ฐ ์๋ค (0) | 2025.10.14 |
| [Go] Gin ํ๋ ์์ํฌ (1) | 2025.09.20 |
| [Go] ํจ์ ๊ณ ๊ธ(๊ฐ๋ณ ์ธ์, defer, ๋๋คํจ์, ํจ์ํ์ ๋ณ์) (0) | 2025.09.19 |
| [Go] Mordern Go ๊ธฐ๋ณธ2(ํจ์, ์ธํฐํ์ด์ค, ๋ฉ์๋, ์๋ฌ) (2) | 2025.05.21 |
๋๊ธ