Menu
Coddy logo textTech

sync.Mutex & sync.RWMutex

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 107개 중 63번째.

채널은 Go에서 고루틴을 조정하는 데 선호되는 방법이지만, 때로는 공유 데이터 자체를 직접 보호해야 합니다. sync 패키지는 한 번에 하나의 고루틴만 리소스에 액세스하도록 보장하는 잠금인 뮤텍스를 제공합니다.

sync.Mutex에는 두 가지 메서드가 있습니다: Lock()Unlock()입니다. 고루틴이 Lock()을 호출하면 배타적 액세스 권한을 얻습니다. Lock()을 호출하는 다른 고루틴은 Unlock()이 호출될 때까지 차단됩니다:

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    c.value++
    c.mu.Unlock()
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

defer c.mu.Unlock()를 사용하면 함수가 일찍 반환되거나 패닉이 발생하더라도 잠금이 해제됩니다. 이는 일반적이고 권장되는 패턴입니다.

읽기가 빈번하지만 쓰기가 드문 경우 sync.RWMutex가 더 나은 성능을 제공합니다. 여러 리더가 동시에 접근할 수 있지만, 라이터는 독점적인 접근 권한을 얻습니다:

type Cache struct {
    mu   sync.RWMutex
    data map[string]string
}

func (c *Cache) Get(key string) string {
    c.mu.RLock()         // 여러 리더 허용
    defer c.mu.RUnlock()
    return c.data[key]
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()          // 쓰기를 위한 배타적 접근
    defer c.mu.Unlock()
    c.data[key] = value
}

읽기 작업에는 RLock()/RUnlock()을 사용하고 쓰기 작업에는 Lock()/Unlock()을 사용하세요. 이렇게 하면 동시에 읽을 수 있으며 쓰기의 안전성이 보장됩니다.

challenge icon

챌린지

쉬움

제품 재고 수준을 추적하는 스레드 안전 inventory system을 만들어 봅시다. mutexes를 사용하여 동시 읽기와 쓰기를 안전하게 처리하고, 여러 operations가 동시에 발생할 때 data integrity를 보장합니다.

코드를 두 파일에 구성합니다:

  • inventory.go: 스레드 안전 inventory management system을 정의합니다.

    제품 수량을 map에 저장하고 sync.RWMutex를 사용하여 access를 보호하는 Inventory struct를 만듭니다. inventory는 다음 operations를 지원해야 합니다:

    • NewInventory() *Inventory - 초기화된 map으로 새로운 inventory를 creates
    • AddStock(product string, quantity int) - 제품의 stock에 quantity를 adds (이것은 data를 modifies하므로 exclusive lock을 사용)
    • GetStock(product string) int - 제품의 current stock을 반환하거나, found되지 않으면 0을 반환 (data를 only 읽으므로 read lock을 사용)
    • RemoveStock(product string, quantity int) bool - sufficient stock이 exists하면 stock에서 quantity를 제거합니다. 성공하면 true, insufficient stock이면 false를 반환 (exclusive lock을 사용)

    lock이 항상 제대로 해제되도록 unlocking에 defer를 사용하는 것을 기억하세요.

  • main.go: operations를 읽고 스레드 안전 inventory를 시연합니다.

    operations의 number를 읽은 다음 각 operation을 처리합니다. 각 operation에는 type(add, get 또는 remove), 제품 이름, 그리고 add/remove operations의 경우 quantity가 포함됩니다.

    각 operation에 대해 결과를 출력합니다:

    • add: Added [quantity] [product]를 출력
    • get: [product]: [stock] in stock을 출력
    • remove: 성공하면 Removed [quantity] [product]를 출력하고, 그렇지 않으면 Insufficient stock for [product]를 출력

다음 입력이 제공됩니다:

  • Line 1: operations의 number (integer)
  • Following lines: 각 operation에 대해:
    • Operation type (add, get 또는 remove)
    • 제품 이름
    • Quantity (addremove operations에만 해당)

예를 들어 다음이 주어지면:

5
add
apples
50
get
apples
remove
apples
30
remove
apples
25
get
apples

출력은 다음과 같아야 합니다:

Added 50 apples
apples: 50 in stock
Removed 30 apples
Insufficient stock for apples
apples: 20 in stock

RWMutex는 여러 GetStock calls가 동시에 읽을 수 있도록 하며, AddStockRemoveStock은 inventory를 modifying할 때 exclusive access를 얻습니다.

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// 작업 수 읽기
	line, _ := reader.ReadString('\n')
	numOps, _ := strconv.Atoi(strings.TrimSpace(line))
	
	// 새 인벤토리 생성
	inventory := NewInventory()
	
	// 각 작업 처리
	for i := 0; i < numOps; i++ {
		// 작업 유형 읽기
		opLine, _ := reader.ReadString('\n')
		opType := strings.TrimSpace(opLine)
		
		// 제품 이름 읽기
		productLine, _ := reader.ReadString('\n')
		product := strings.TrimSpace(productLine)
		
		// TODO: 각 작업 유형 처리 (add, get, remove)
		// "add"와 "remove"의 경우, 입력에서 수량을 읽기
		// 적절한 인벤토리 메서드 호출
		// 챌린지 설명에 따라 결과 출력
		
		switch opType {
		case "add":
			// TODO: 수량 읽기, 재고 추가, 결과 출력
			
		case "get":
			// TODO: 재고 가져오기, 결과 출력
			
		case "remove":
			// TODO: 수량 읽기, 재고 제거, 적절한 결과 출력
			
		}
	}
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 Go 컴파일러