프로세스, 스레드, 병렬성, 동시성
프로세스는 운영체제에서 실행 중인 프로그램 인스턴스로, 자원 할당의 기본 단위입니다. 5가지 상태(초기, 실행, 대기, 준비, 종료)를 가지며 동적 실행 환경을 제공합니다.
스레드는 프로세스 내 실행 단위로, 독립적으로 실행 가능한 최소 작업 단위입니다. 한 프로세스는 여러 스레드를 생성할 수 있습니다.
동시성은 단일 CPU에서 여러 스레드가 자원을 경쟁하며 시간 분할로 실행되는 것을 의미합니다. 특정 시점에는 하나의 스레드만 실행됩니다.
병렬성은 다중 CPU 코어에서 여러 스레드가 동시에 실행되는 것을 말합니다. 코어 수보다 스레드가 많을 경우 동시성과 병렬성이 혼합됩니다.
고루틴과 메인 스레드
Go에서 메인 스레드는 프로세스/스레드로 간주되며, 고루틴이라는 경량 스레드를 생성합니다. 고루틴은 2KB 정도의 작은 메모리를 사용하며, go 키워드로 생성됩니다.
package main
import (
"fmt"
"time"
)
func printNumbers() {
for i := 1; i <= 5; i++ {
fmt.Println("고루틴:", i)
time.Sleep(50 * time.Millisecond)
}
}
func main() {
go printNumbers()
for i := 1; i <= 5; i++ {
fmt.Println("메인:", i)
time.Sleep(50 * time.Millisecond)
}
}
sync.WaitGroup 활용
고루틴 실행 완료 대기를 위해 sync.WaitGroup을 사용합니다:
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func task(id int) {
defer wg.Done()
for i := 0; i < 3; i++ {
fmt.Printf("작업 %d: %d\n", id, i)
time.Sleep(50 * time.Millisecond)
}
}
func main() {
for i := 0; i < 4; i++ {
wg.Add(1)
go task(i)
}
wg.Wait()
fmt.Println("모든 작업 완료")
}
CPU 코어 설정
런타임 설정으로 병렬 실행에 사용할 CPU 코어 수를 지정합니다:
package main
import (
"fmt"
"runtime"
)
func main() {
cores := runtime.NumCPU()
fmt.Println("사용 가능한 코어:", cores)
runtime.GOMAXPROCS(cores - 1)
}
채널 기초
채널은 고루틴 간 통신을 위한 파이프라인으로 FIFO 방식으로 동작합니다:
package main
import "fmt"
func main() {
msgChan := make(chan string, 2)
msgChan <- "첫 번째 메시지"
msgChan <- "두 번째 메시지"
fmt.Println(<-msgChan)
fmt.Println(<-msgChan)
close(msgChan)
}
고루틴과 채널 조합
생산자-소비자 패턴 구현 예시:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func producer(ch chan<- int) {
defer wg.Done()
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func consumer(ch <-chan int) {
defer wg.Done()
for num := range ch {
fmt.Println("수신:", num)
}
}
func main() {
dataChan := make(chan int, 3)
wg.Add(2)
go producer(dataChan)
go consumer(dataChan)
wg.Wait()
}
동시성 제어
맵 동시 접근 시 뮤텍스로 보호:
package main
import (
"fmt"
"sync"
)
var (
resultMap = make(map[int]int)
mu sync.Mutex
wg sync.WaitGroup
)
func computeFactorial(n int) {
defer wg.Done()
res := 1
for i := 1; i <= n; i++ {
res *= i
}
mu.Lock()
resultMap[n] = res
mu.Unlock()
}
func main() {
for i := 1; i <= 10; i++ {
wg.Add(1)
go computeFactorial(i)
}
wg.Wait()
fmt.Println("결과:", resultMap)
}
고루틴 패닉 처리
recover로 고루틴 내 패닉 복구:
package main
import "fmt"
func safeTask() {
defer func() {
if r := recover(); r != nil {
fmt.Println("복구:", r)
}
}()
// 패닉 발생 코드
panic("의도된 오류")
}
func main() {
go safeTask()
// 다른 작업 계속 실행
}