๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด/Go

[Go] ๊ณ ๋ฃจํ‹ด(Goroutine)

by ์„œ์•„๋ž‘๐Ÿ˜ƒ 2025. 10. 13.


๋“ค์–ด๊ฐ€๋ฉฐ

๊ณ ๋ฃจํ‹ด์€ Go์˜ ๋น„๋™๊ธฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ž…๋‹ˆ๋‹ค. Go์˜ ์ตœ๋Œ€ ๋งค๋ ฅํฌ์ธํŠธ๋ผ๊ณ  ๋ด๋„ ๊ณผ์–ธ์ด ์•„๋‹ˆ์ฃ . ๊ณ ๋ฃจํ‹ด์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ, ๋ณ‘๋ ฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค. Go๋Š” ๋น„๋™๊ธฐ ์ธก๋ฉด์—์„œ ๋‹ค๋ฅธ ์–ธ์–ด์™€ ๋‹ค๋ฅด๊ฒŒ CSP(Communicating Sequential Processes)๋ผ๋Š” ํ•™์ˆ ์  ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ณ ๋ฃจํ‹ด๊ณผ ์ฑ„๋„๋ฐฉ์‹์ด ํƒ„์ƒํ•˜๊ฒŒ ๋์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ณ ๋ฃจํ‹ด์œผ๋กœ ์‹คํ–‰ํ•˜๋Š” ์Šค๋ ˆ๋“œ ๋‹จ์œ„๊ฐ€ ๊ฒฝ๋Ÿ‰(2KB)์ด๊ธฐ ๋•Œ๋ฌธ์— 10๋งŒ๊ฐœ์˜ task๋„ ๊ณ ๋ฃจํ‹ด์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์ผ๋ฐ˜์ ์ธ OS ์Šค๋ ˆ๋“œ๋Š” 1MB ์šฉ๋Ÿ‰). 

 

 

๊ณ ๋ฃจํ‹ด ์Šค์ผ€์ค„๋Ÿฌ

์ถœ์ฒ˜: https://ykarma1996.tistory.com/188

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           |
+-----------------------------+
 

๊ธฐ๋ณธ ํ๋ฆ„:

  1. OS Thread(M)๋Š” P๋ฅผ ์ ์œ ํ•ด์•ผ๋งŒ goroutine(G)์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. ๊ฐ P๋Š” ์ž์ฒด run queue๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด G๋ฅผ ๋กœ์ปฌ์—์„œ ์šฐ์„  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  3. ๋กœ์ปฌ queue๊ฐ€ ๋น„๋ฉด, ๋‹ค๋ฅธ P์˜ queue์—์„œ work stealing์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  4. ์‹คํ–‰ ์ค‘ 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"

 

๋Œ“๊ธ€