GoSuda

Go Concurrency Starter Pack

By snowmerak
views ...

κ°œμš”

짧은 μ†Œκ°œ

κ³  μ–Έμ–΄μ—λŠ” λ§Žμ€ λ™μ‹œμ„± 관리λ₯Ό μœ„ν•œ 도ꡬ가 μžˆμŠ΅λ‹ˆλ‹€. 이 μ•„ν‹°ν΄μ—μ„œλŠ” κ·Έ 쀑 일뢀와 νŠΈλ¦­λ“€μ„ μ†Œκ°œν•΄λ“œλ¦¬λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

고루틴?

goroutine은 κ³  μ–Έμ–΄μ—μ„œ μ§€μ›ν•˜λŠ” μƒˆλ‘œμš΄ ν˜•μ‹μ˜ λ™μ‹œμ„± λͺ¨λΈμž…λ‹ˆλ‹€. 일반적으둜 ν”„λ‘œκ·Έλž¨μ€ λ™μ‹œμ— μ—¬λŸ¬ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ OSμ—κ²Œμ„œ OS μŠ€λ ˆλ“œλ₯Ό λ°›μ•„μ„œ, μ½”μ–΄ 수만큼 λ³‘λ ¬μ μœΌλ‘œ μž‘μ—…μ„ μˆ˜ν–‰ν•©λ‹ˆλ‹€. 그리고 더 μž‘μ€ λ‹¨μœ„μ˜ λ™μ‹œμ„±μ„ μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄μ„œλŠ” μœ μ €λžœλ“œμ—μ„œ κ·Έλ¦° μŠ€λ ˆλ“œλ₯Ό μƒμ„±ν•˜μ—¬, ν•˜λ‚˜μ˜ OS μŠ€λ ˆλ“œ λ‚΄μ—μ„œ μ—¬λŸ¬ κ·Έλ¦° μŠ€λ ˆλ“œκ°€ λŒμ•„κ°€λ©° μž‘μ—…μ„ μˆ˜ν–‰ν•˜λ„λ‘ ν•©λ‹ˆλ‹€. ν•˜μ§€λ§Œ κ³ λ£¨ν‹΄μ˜ κ²½μš°μ—” μ΄λŸ¬ν•œ ν˜•νƒœμ˜ κ·Έλ¦° μŠ€λ ˆλ“œλ₯Ό λ”μš± μž‘κ³  효율적으둜 λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 고루틴은 μŠ€λ ˆλ“œλ³΄λ‹€ 더 적은 λ©”λͺ¨λ¦¬λ₯Ό μ‚¬μš©ν•˜λ©°, μŠ€λ ˆλ“œλ³΄λ‹€ 더 λΉ λ₯΄κ²Œ μƒμ„±λ˜κ³  ꡐ체될 수 μžˆμŠ΅λ‹ˆλ‹€.

고루틴을 μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ‹¨μˆœνžˆ go ν‚€μ›Œλ“œλ§Œ μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€. μ΄λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜λŠ” κ³Όμ •μ—μ„œ μ§κ΄€μ μœΌλ‘œ 동기 μ½”λ“œλ₯Ό 비동기 μ½”λ“œλ‘œ μ‹€ν–‰ν•  수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "fmt"
 5    "time"
 6)
 7
 8func main() {
 9    ch := make(chan struct{})
10    go func() {
11        defer close(ch)
12        time.Sleep(1 * time.Second)
13        fmt.Println("Hello, World!")
14    }()
15
16    fmt.Println("Waiting for goroutine...")
17    for range ch {}
18}

이 μ½”λ“œλŠ” κ°„λ‹¨ν•˜κ²Œ 1초 μ‰¬μ—ˆλ‹€κ°€ Hello, World!λ₯Ό 좜λ ₯ν•˜λŠ” 동기식 μ½”λ“œλ₯Ό 비동기 νλ¦„μœΌλ‘œ λ³€κ²½ν•©λ‹ˆλ‹€. μ§€κΈˆμ˜ μ˜ˆμ œλŠ” κ°„λ‹¨ν•˜μ§€λ§Œ, 쑰금 λ³΅μž‘ν•œ μ½”λ“œλ₯Ό 동기 μ½”λ“œμ—μ„œ 비동기 μ½”λ“œλ‘œ λ³€κ²½ν•˜κ²Œ 되면, μ½”λ“œμ˜ 가독성과 κ°€μ‹œμ„±, 이해도가 기쑴의 async awaitλ‚˜ promise 같은 방식보닀 λ”μš± μ’‹μ•„μ§‘λ‹ˆλ‹€.

λ‹€λ§Œ λ§Žμ€ κ²½μš°μ—, μ΄λŸ¬ν•œ 동기 μ½”λ“œλ₯Ό λ‹¨μˆœνžˆ λΉ„λ™κΈ°λ‘œ ν˜ΈμΆœν•˜λŠ” 흐름과 fork & joinκ³Ό 같은 흐름(마치 λΆ„ν•  정볡과 μœ μ‚¬ν•œ 흐름)을 μ΄ν•΄ν•˜μ§€ λͺ»ν•œ μƒνƒœμ—μ„  μ•ˆ 쒋은 고루틴 μ½”λ“œκ°€ λ§Œλ“€μ–΄μ§€κΈ°λ„ ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ κ²½μš°μ— λŒ€λΉ„ν•  수 λͺ‡κ°€μ§€ 방법과 기법을 이 μ•„ν‹°ν΄μ—μ„œ μ†Œκ°œν•˜λ„λ‘ ν•˜κ² μŠ΅λ‹ˆλ‹€.

λ™μ‹œμ„± 관리

context

첫번째 관리 κΈ°λ²•μœΌλ‘œ contextκ°€ λ“±μž₯ν•˜λŠ” 건 μ˜μ™ΈμΌ 수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ κ³  μ–Έμ–΄μ—μ„œ contextλŠ” λ‹¨μˆœν•œ μ·¨μ†Œ κΈ°λŠ₯을 λ„˜μ–΄μ„œ, 전체 μž‘μ—… 트리λ₯Ό κ΄€λ¦¬ν•˜λŠ” 데에 νƒμ›”ν•œ 역할을 ν•©λ‹ˆλ‹€. λ§Œμ•½ λͺ¨λ₯΄μ‹œλŠ” 뢄듀을 μœ„ν•΄ κ°„λ‹¨νžˆ ν•΄λ‹Ή νŒ¨ν‚€μ§€λ₯Ό μ„€λͺ…ν•˜κ² μŠ΅λ‹ˆλ‹€.

 1package main
 2
 3func main() {
 4    ctx, cancel := context.WithCancel(context.Background())
 5    defer cancel()
 6
 7    go func() {
 8        <-ctx.Done()
 9        fmt.Println("Context is done!")
10    }()
11
12    time.Sleep(1 * time.Second)
13
14    cancel()
15
16    time.Sleep(1 * time.Second)
17}

μœ„ μ½”λ“œλŠ” contextλ₯Ό μ‚¬μš©ν•˜μ—¬, 1초 후에 Context is done!λ₯Ό 좜λ ₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. contextλŠ” Done() λ©”μ†Œλ“œλ₯Ό 톡해 μ·¨μ†Œ μ—¬λΆ€λ₯Ό 확인할 수 있으며, WithCancel, WithTimeout, WithDeadline, WithValue λ“±μ˜ λ©”μ†Œλ“œλ₯Ό 톡해 λ‹€μ–‘ν•œ μ·¨μ†Œ 방법을 μ œκ³΅ν•©λ‹ˆλ‹€.

κ°„λ‹¨ν•œ μ˜ˆμ‹œλ₯Ό λ§Œλ“€μ–΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. λ§Œμ•½ μ—¬λŸ¬λΆ„λ“€μ΄ μ–΄λ–€ 데이터λ₯Ό κ°€μ Έμ˜€κΈ° μœ„ν•΄ aggregator νŒ¨ν„΄μ„ μ‚¬μš©ν•˜μ—¬, user, post, commentλ₯Ό κ°€μ Έμ˜€λŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•œλ‹€κ³  κ°€μ •ν•΄λ΄…μ‹œλ‹€. 그리고 λͺ¨λ“  μš”μ²­μ΄ 2초 내에 μ΄λ£¨μ–΄μ Έμ•Όν•œλ‹€λ©΄, λ‹€μŒκ³Ό 같이 μž‘μ„±ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3func main() {
 4    ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second)
 5    defer cancel()
 6
 7    ch := make(chan struct{})
 8    go func() {
 9        defer close(ch)
10        user := getUser(ctx)
11        post := getPost(ctx)
12        comment := getComment(ctx)
13
14        fmt.Println(user, post, comment)
15    }()
16
17    select {
18    case <-ctx.Done():
19        fmt.Println("Timeout!")
20    case <-ch:
21        fmt.Println("All data is fetched!")
22    }
23}

μœ„ μ½”λ“œλŠ” 2초 내에 λͺ¨λ“  데이터λ₯Ό κ°€μ Έμ˜€μ§€ λͺ»ν•˜λ©΄ Timeout!을 좜λ ₯ν•˜κ³ , λͺ¨λ“  데이터λ₯Ό κ°€μ Έμ˜€λ©΄ All data is fetched!λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ contextλ₯Ό μ‚¬μš©ν•˜λ©΄, μ—¬λŸ¬ 고루틴이 λ™μž‘ν•˜λŠ” μ½”λ“œμ—μ„œλ„ μ·¨μ†Œμ™€ νƒ€μž„μ•„μ›ƒμ„ μ‰½κ²Œ 관리할 수 μžˆμŠ΅λ‹ˆλ‹€.

이와 κ΄€λ ¨λœ λ‹€μ–‘ν•œ context κ΄€λ ¨ ν•¨μˆ˜μ™€ λ©”μ„œλ“œκ°€ godoc contextμ—μ„œ 확인 κ°€λŠ₯ν•©λ‹ˆλ‹€. κ°„λ‹¨ν•œ 것은 ν•™μŠ΅ν•˜μ—¬ 편히 μ΄μš©ν•  수 있게 λ˜μ…¨μœΌλ©΄ ν•©λ‹ˆλ‹€.

channel

unbuffered channel

channel은 고루틴 κ°„μ˜ 톡신을 μœ„ν•œ λ„κ΅¬μž…λ‹ˆλ‹€. channel은 make(chan T)둜 생성할 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λ•Œ, TλŠ” ν•΄λ‹Ή channel이 전달할 λ°μ΄ν„°μ˜ νƒ€μž…μž…λ‹ˆλ‹€. channel은 <-둜 데이터λ₯Ό μ£Όκ³  받을 수 있으며, close둜 channel을 닫을 수 μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3func main() {
 4    ch := make(chan int)
 5    go func() {
 6        ch <- 1
 7        ch <- 2
 8        close(ch)
 9    }()
10
11    for i := range ch {
12        fmt.Println(i)
13    }
14}

μœ„ μ½”λ“œλŠ” channel을 μ‚¬μš©ν•˜μ—¬ 1κ³Ό 2λ₯Ό 좜λ ₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” λ‹¨μˆœν•˜κ²Œ channel에 값을 보내고 λ°›λŠ” κ²ƒλ§Œμ„ 보여주고 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ channel은 이보닀 더 λ§Žμ€ κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€. λ¨Όμ € buffered channelκ³Ό unbuffered channel에 λŒ€ν•΄ μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€. μ‹œμž‘ν•˜κΈ°μ— μ•žμ„œ μœ„μ— μž‘μ„±λœ μ˜ˆμ œλŠ” unbuffered channel둜, 채널에 데이터λ₯Ό λ³΄λ‚΄λŠ” 행동과 데이터λ₯Ό λ°›λŠ” 행동이 λ™μ‹œμ— μ΄λ£¨μ–΄μ Έμ•Όν•©λ‹ˆλ‹€. λ§Œμ•½ μ΄λŸ¬ν•œ 행동이 λ™μ‹œμ— 이루어지지 μ•ŠλŠ”λ‹€λ©΄, λ°λ“œλ½μ΄ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

buffered channel

λ§Œμ•½ μœ„ μ½”λ“œκ°€ λ‹¨μˆœ 좜λ ₯이 μ•„λ‹ˆλΌ 무거운 μž‘μ—…μ„ μˆ˜ν–‰ν•˜λŠ” ν”„λ‘œμ„ΈμŠ€ 2가지라면 μ–΄λ–€κ°€μš”? λ‘λ²ˆμ§Έ ν”„λ‘œμ„ΈμŠ€κ°€ μ½μ–΄μ„œ 처리λ₯Ό μˆ˜ν–‰ν•˜λ‹€κ°€ μž₯κΈ°κ°„ 행이 κ±Έλ¦°λ‹€λ©΄, 첫번째 ν”„λ‘œμ„ΈμŠ€λ„ ν•΄λ‹Ή μ‹œκ°„ λ™μ•ˆ λ©ˆμΆ”κ²Œ 될 κ²ƒμž…λ‹ˆλ‹€. μ €ν¬λŠ” μ΄λŸ¬ν•œ 상황을 λ°©μ§€ν•˜κΈ° μœ„ν•΄ buffered channel을 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3func main() {
 4    ch := make(chan int, 2)
 5    go func() {
 6        ch <- 1
 7        ch <- 2
 8        close(ch)
 9    }()
10
11    for i := range ch {
12        fmt.Println(i)
13    }
14}

μœ„ μ½”λ“œλŠ” buffered channel을 μ‚¬μš©ν•˜μ—¬ 1κ³Ό 2λ₯Ό 좜λ ₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” buffered channel을 μ‚¬μš©ν•˜μ—¬, channel에 데이터λ₯Ό λ³΄λ‚΄λŠ” 행동과 데이터λ₯Ό λ°›λŠ” 행동이 λ™μ‹œμ— 이루어지지 μ•Šμ•„λ„ λ˜λ„λ‘ λ§Œλ“€μ—ˆμŠ΅λ‹ˆλ‹€. μ΄λ ‡κ²Œ 채널에 버퍼λ₯Ό λ‘κ²Œ 되면, ν•΄λ‹Ή 길이만큼 μ—¬μœ κ°€ 생겨 ν›„μˆœμœ„ μž‘μ—…μ˜ 영ν–₯μœΌλ‘œμΈν•΄ λ°œμƒν•˜λŠ” μž‘μ—… 지연을 λ°©μ§€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

select

μ—¬λŸ¬ 채널을 λ‹€λ£° λ•Œ, select 문법을 μ‚¬μš©ν•˜λ©΄ μ‰½κ²Œ fan-in ꡬ쑰λ₯Ό κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "fmt"
 5    "time"
 6)
 7
 8func main() {
 9    ch1 := make(chan int, 10)
10    ch2 := make(chan int, 10)
11    ch3 := make(chan int, 10)
12
13    go func() {
14        for {
15            ch1 <- 1
16            time.Sleep(1 * time.Second)
17        }
18    }()
19    go func() {
20        for {
21            ch2 <- 2
22            time.Sleep(2 * time.Second)
23        }
24    }()
25    go func() {
26        for {
27            ch3 <- 3
28            time.Sleep(3 * time.Second)
29        }
30    }()
31
32    for i := 0; i < 3; i++ {
33        select {
34        case v := <-ch1:
35            fmt.Println(v)
36        case v := <-ch2:
37            fmt.Println(v)
38        case v := <-ch3:
39            fmt.Println(v)
40        }
41    }
42}

μœ„ μ½”λ“œλŠ” 주기적으둜 1, 2, 3을 μ „λ‹¬ν•˜λŠ” 3개의 채널을 λ§Œλ“€κ³ , selectλ₯Ό μ‚¬μš©ν•˜μ—¬ μ±„λ„μ—μ„œ 값을 λ°›μ•„ 좜λ ₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ selectλ₯Ό μ‚¬μš©ν•˜λ©΄, μ—¬λŸ¬ μ±„λ„μ—μ„œ λ™μ‹œμ— 데이터λ₯Ό 전달 λ°›μœΌλ©΄μ„œ, μ±„λ„μ—μ„œ 값을 λ°›λŠ” λŒ€λ‘œ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

for range

channel은 for rangeλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‰½κ²Œ 데이터λ₯Ό 받을 수 μžˆμŠ΅λ‹ˆλ‹€. for rangeλ₯Ό 채널에 μ‚¬μš©ν•˜κ²Œ 되면 ν•΄λ‹Ή 채널에 데이터가 좔가될 λ•Œλ§ˆλ‹€ λ™μž‘ν•˜κ²Œ 되며, 채널이 λ‹«νžˆλ©΄ 루프λ₯Ό μ’…λ£Œν•©λ‹ˆλ‹€.

 1package main
 2
 3func main() {
 4    ch := make(chan int)
 5    go func() {
 6        ch <- 1
 7        ch <- 2
 8        close(ch)
 9    }()
10
11    for i := range ch {
12        fmt.Println(i)
13    }
14}

μœ„ μ½”λ“œλŠ” channel을 μ‚¬μš©ν•˜μ—¬ 1κ³Ό 2λ₯Ό 좜λ ₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” for rangeλ₯Ό μ‚¬μš©ν•˜μ—¬ 채널에 데이터가 좔가될 λ•Œλ§ˆλ‹€ 데이터λ₯Ό λ°›μ•„ 좜λ ₯ν•©λ‹ˆλ‹€. 그리고 채널이 λ‹«νžˆλ©΄ 루프λ₯Ό μ’…λ£Œν•©λ‹ˆλ‹€.

μœ„μ— λͺ‡λ²ˆ μž‘μ„±ν•œ λŒ€λ‘œ 이 문법은 λ‹¨μˆœ 동기화 μˆ˜λ‹¨μ— μ‚¬μš©ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3func main() {
 4    ch := make(chan struct{})
 5    go func() {
 6        defer close(ch)
 7        time.Sleep(1 * time.Second)
 8        fmt.Println("Hello, World!")
 9    }()
10
11    fmt.Println("Waiting for goroutine...")
12    for range ch {}
13}

μœ„ μ½”λ“œλŠ” 1초 μ‰¬μ—ˆλ‹€κ°€ Hello, World!λ₯Ό 좜λ ₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” channel을 μ‚¬μš©ν•˜μ—¬ 동기식 μ½”λ“œλ₯Ό 비동기식 μ½”λ“œλ‘œ λ³€κ²½ν•˜μ˜€μŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ channel을 μ‚¬μš©ν•˜λ©΄, 동기식 μ½”λ“œλ₯Ό 비동기식 μ½”λ“œλ‘œ μ‰½κ²Œ λ³€κ²½ν•˜κ³ , join 지점을 μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

etc

  1. nil channel에 데이터λ₯Ό λ³΄λ‚΄κ±°λ‚˜ λ°›μœΌλ©΄, λ¬΄ν•œ 루프에 λΉ μ Έ λ°λ“œλ½μ΄ λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  2. 채널을 닫은 후에 데이터λ₯Ό 보내면, panic이 λ°œμƒν•©λ‹ˆλ‹€.
  3. 채널을 ꡳ이 λ‹«μ§€ μ•Šμ•„λ„, GCκ°€ μˆ˜κ±°ν•˜λ©΄μ„œ 채널을 λ‹«μŠ΅λ‹ˆλ‹€.

mutex

spinlock

spinlock은 λ°˜λ³΅λ¬Έμ„ 돌며 κ³„μ†ν•΄μ„œ 락을 μ‹œλ„ν•˜λŠ” 동기화 λ°©λ²•μž…λ‹ˆλ‹€. κ³  언어에선 포인터λ₯Ό μ‚¬μš©ν•˜μ—¬ μ‰½κ²Œ μŠ€ν•€λ½μ„ κ΅¬ν˜„ν•΄λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

 1package spinlock
 2
 3import (
 4    "runtime"
 5    "sync/atomic"
 6)
 7
 8type SpinLock struct {
 9    lock uintptr
10}
11
12func (s *SpinLock) Lock() {
13    // Continua a provare a ottenere il lock finchΓ© non ci riesce
14    for !atomic.CompareAndSwapUintptr(&s.lock, 0, 1) {
15        // Cede il controllo al scheduler per evitare lo spinning attivo
16        runtime.Gosched()
17    }
18}
19
20func (s *SpinLock) Unlock() {
21    // Rilascia il lock impostandolo a 0
22    atomic.StoreUintptr(&s.lock, 0)
23}
24
25func NewSpinLock() *SpinLock {
26    // Crea e restituisce una nuova istanza di SpinLock
27    return &SpinLock{}
28}

μœ„ μ½”λ“œλŠ” spinlock νŒ¨ν‚€μ§€λ₯Ό κ΅¬ν˜„ν•œ μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” sync/atomic νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν•˜μ—¬ SpinLock을 κ΅¬ν˜„ν•˜μ˜€μŠ΅λ‹ˆλ‹€. Lock λ©”μ„œλ“œμ—μ„œλŠ” atomic.CompareAndSwapUintptrλ₯Ό μ‚¬μš©ν•˜μ—¬ 락을 μ‹œλ„ν•˜κ³ , Unlock λ©”μ„œλ“œμ—μ„œλŠ” atomic.StoreUintptrλ₯Ό μ‚¬μš©ν•˜μ—¬ 락을 ν•΄μ œν•©λ‹ˆλ‹€. 이 방식은 쉬지 μ•Šκ³  락을 μ‹œλ„ν•˜κΈ° λ•Œλ¬Έμ—, 락을 얻을 λ•ŒκΉŒμ§€ κ³„μ†ν•΄μ„œ CPUλ₯Ό μ‚¬μš©ν•˜κ²Œ λ˜μ–΄, λ¬΄ν•œ 루프에 빠질 수 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ, spinlock은 λ‹¨μˆœν•œ 동기화에 μ‚¬μš©ν•˜κ±°λ‚˜, 짧은 μ‹œκ°„ λ™μ•ˆλ§Œ μ‚¬μš©ν•˜λŠ” κ²½μš°μ— μ‚¬μš©ν•˜λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

sync.Mutex

mutexλŠ” 고루틴 κ°„μ˜ 동기화λ₯Ό μœ„ν•œ λ„κ΅¬μž…λ‹ˆλ‹€. sync νŒ¨ν‚€μ§€μ—μ„œ μ œκ³΅ν•˜λŠ” mutexλŠ” Lock, Unlock, RLock, RUnlock λ“±μ˜ λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. mutexλŠ” sync.Mutex둜 생성할 수 있으며, sync.RWMutex둜 읽기/μ“°κΈ° 락을 μ‚¬μš©ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "sync"
 5)
 6
 7func main() {
 8    var mu sync.Mutex
 9    var count int
10
11    go func() {
12        mu.Lock()
13        count++
14        mu.Unlock()
15    }()
16
17    mu.Lock()
18    count++
19    mu.Unlock()
20
21    println(count)
22}

μœ„ μ½”λ“œμ—μ„œλŠ” 거의 λ™μ‹œμ— 두 고루틴이 λ™μΌν•œ count λ³€μˆ˜μ— μ ‘κ·Όν•˜κ²Œ λ©λ‹ˆλ‹€. μ΄λ•Œ, mutexλ₯Ό μ‚¬μš©ν•˜μ—¬ count λ³€μˆ˜μ— μ ‘κ·Όν•˜λŠ” μ½”λ“œλ₯Ό μž„κ³„ μ˜μ—­μœΌλ‘œ λ§Œλ“€μ–΄μ£Όλ©΄, count λ³€μˆ˜μ— λŒ€ν•œ λ™μ‹œ 접근을 막을 수 μžˆμŠ΅λ‹ˆλ‹€. 그러면 이 μ½”λ“œλŠ” λͺ‡λ²ˆμ„ μ‹€ν–‰ν•˜λ“  λ™μΌν•˜κ²Œ 2λ₯Ό 좜λ ₯ν•˜κ²Œ λ©λ‹ˆλ‹€.

sync.RWMutex

sync.RWMutexλŠ” 읽기 락과 μ“°κΈ° 락을 κ΅¬λΆ„ν•˜μ—¬ μ‚¬μš©ν•  수 μžˆλŠ” mutexμž…λ‹ˆλ‹€. RLock, RUnlock λ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 읽기 락을 κ±Έκ³  ν•΄μ œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 1package cmap
 2
 3import (
 4    "sync"
 5)
 6
 7type ConcurrentMap[K comparable, V any] struct {
 8    sync.RWMutex
 9    data map[K]V
10}
11
12func (m *ConcurrentMap[K, V]) Get(key K) (V, bool) {
13    m.RLock()
14    defer m.RUnlock()
15
16    value, ok := m.data[key]
17    return value, ok
18}
19
20func (m *ConcurrentMap[K, V]) Set(key K, value V) {
21    m.Lock()
22    defer m.Unlock()
23
24    m.data[key] = value
25}

μœ„ μ½”λ“œλŠ” sync.RWMutexλ₯Ό μ‚¬μš©ν•˜μ—¬ ConcurrentMap을 κ΅¬ν˜„ν•œ μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” Get λ©”μ†Œλ“œμ—μ„œ 읽기 락을 κ±Έκ³ , Set λ©”μ†Œλ“œμ—μ„œ μ“°κΈ° 락을 κ±Έμ–΄ data 맡에 μ•ˆμ „ν•˜κ²Œ μ ‘κ·Όν•˜κ³  μˆ˜μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 읽기 락이 ν•„μš”ν•œ μ΄μœ λŠ” λ‹¨μˆœν•œ 읽기 μž‘μ—…μ΄ λ§Žμ€ 경우, μ“°κΈ° 락을 κ±Έμ§€ μ•Šκ³  읽기 락만 κ±Έμ–΄ μ—¬λŸ¬ 고루틴이 λ™μ‹œμ— 읽기 μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλ„λ‘ ν•˜κΈ° μœ„ν•¨μž…λ‹ˆλ‹€. 이λ₯Ό 톡해, ꡳ이 μƒνƒœμ˜ 변경이 μ—†μ–΄μ„œ μ“°κΈ° 락을 κ±Έμ§€ μ•Šμ•„λ„ λ˜λŠ” κ²½μš°μ—λŠ” 읽기 락만 κ±Έμ–΄ μ„±λŠ₯을 ν–₯μƒμ‹œν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€.

fakelock

fakelock은 sync.Lockerλ₯Ό κ΅¬ν˜„ν•˜λŠ” κ°„λ‹¨ν•œ νŠΈλ¦­μž…λ‹ˆλ‹€. 이 κ΅¬μ‘°μ²΄λŠ” sync.Mutex와 λ™μΌν•œ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜μ§€λ§Œ, μ‹€μ œ λ™μž‘μ€ ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.

 1package fakelock
 2
 3type FakeLock struct{}
 4
 5func (f *FakeLock) Lock() {
 6    // Questo metodo non esegue alcuna operazione di blocco reale.
 7}
 8
 9func (f *FakeLock) Unlock() {
10    // Questo metodo non esegue alcuna operazione di sblocco reale.
11}

μœ„ μ½”λ“œλŠ” fakelock νŒ¨ν‚€μ§€λ₯Ό κ΅¬ν˜„ν•œ μ½”λ“œμž…λ‹ˆλ‹€. 이 νŒ¨ν‚€μ§€λŠ” sync.Lockerλ₯Ό κ΅¬ν˜„ν•˜μ—¬ Lock, Unlock λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜μ§€λ§Œ, μ‹€μ œλ‘œλŠ” 아무 λ™μž‘λ„ ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ™œ μ΄λŸ¬ν•œ μ½”λ“œκ°€ ν•„μš”ν•œμ§€λŠ” κΈ°νšŒκ°€ 되면 μ„œμˆ ν•˜κ² μŠ΅λ‹ˆλ‹€.

waitgroup

sync.WaitGroup

sync.WaitGroup은 κ³ λ£¨ν‹΄μ˜ μž‘μ—…μ΄ λͺ¨λ‘ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬λŠ” λ„κ΅¬μž…λ‹ˆλ‹€. Add, Done, Wait λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•˜λ©°, Add λ©”μ†Œλ“œλ‘œ κ³ λ£¨ν‹΄μ˜ 개수λ₯Ό μΆ”κ°€ν•˜κ³ , Done λ©”μ†Œλ“œλ‘œ κ³ λ£¨ν‹΄μ˜ μž‘μ—…μ΄ λλ‚¬μŒμ„ μ•Œλ¦½λ‹ˆλ‹€. 그리고 Wait λ©”μ†Œλ“œλ‘œ λͺ¨λ“  κ³ λ£¨ν‹΄μ˜ μž‘μ—…μ΄ 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦½λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "sync"
 5    "sync/atomic"
 6)
 7
 8func main() {
 9    wg := sync.WaitGroup{}
10    c := atomic.Int64{}
11
12    for i := 0; i < 100 ; i++ {
13        wg.Add(1)
14        go func() {
15            defer wg.Done()
16            c.Add(1)
17        }()
18    }
19
20    wg.Wait()
21    println(c.Load())
22}

μœ„ μ½”λ“œλŠ” sync.WaitGroup을 μ‚¬μš©ν•˜μ—¬ 100개의 고루틴이 λ™μ‹œμ— c λ³€μˆ˜μ— 값을 λ”ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” sync.WaitGroup을 μ‚¬μš©ν•˜μ—¬ λͺ¨λ“  고루틴이 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦° ν›„, c λ³€μˆ˜μ— λ”ν•œ 값을 좜λ ₯ν•©λ‹ˆλ‹€. λ‹¨μˆœν•˜κ²Œ λͺ‡λͺ‡κ°œμ˜ μž‘μ—…μ„ fork & joinν•˜λŠ” κ²½μš°μ—” μ±„λ„λ§Œμ„ μ΄μš©ν•΄λ„ μΆ©λΆ„ν•˜μ§€λ§Œ, λ‹€λŸ‰μ˜ μž‘μ—…μ„ fork & joinν•˜λŠ” κ²½μš°μ—” sync.WaitGroup을 μ‚¬μš©ν•˜λŠ” 것도 쒋은 μ„ νƒμ§€μž…λ‹ˆλ‹€.

with slice

μŠ¬λΌμ΄μŠ€μ™€ ν•¨κ»˜ 쓰인닀면, waitgroup은 락 없이 ν›Œλ₯­ν•œ λ™μ‹œ μ‹€ν–‰ μž‘μ—…μ„ κ΄€λ¦¬ν•˜λŠ” 도ꡬ가 될 수 μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3import (
 4	"fmt"
 5	"sync"
 6    "rand"
 7)
 8
 9func main() {
10	var wg sync.WaitGroup
11	arr := [10]int{}
12
13	for i := 0; i < 10; i++ {
14		wg.Add(1)
15		go func(id int) {
16			defer wg.Done()
17
18			arr[id] = rand.Intn(100)
19		}(i)
20	}
21
22	wg.Wait()
23	fmt.Println("Done")
24
25    for i, v := range arr {
26        fmt.Printf("arr[%d] = %d\n", i, v)
27    }
28}

μœ„ μ½”λ“œλŠ” waitgroupλ§Œμ„ μ‚¬μš©ν•˜μ—¬ 각 고루틴이 λ™μ‹œμ— 10개의 랜덀 μ •μˆ˜λ₯Ό μƒμ„±ν•˜μ—¬, 할당받은 μΈλ±μŠ€μ— μ €μž₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” waitgroup을 μ‚¬μš©ν•˜μ—¬ λͺ¨λ“  고루틴이 끝날 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦° ν›„, Done을 좜λ ₯ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ waitgroup을 μ‚¬μš©ν•˜λ©΄, μ—¬λŸ¬ 고루틴이 λ™μ‹œμ— μž‘μ—…μ„ μˆ˜ν–‰ν•˜κ³ , λͺ¨λ“  고루틴이 끝날 λ•ŒκΉŒμ§€ 락 없이 데이터λ₯Ό μ €μž₯ν•˜κ³ , μž‘μ—… μ’…λ£Œ 후에 μΌκ΄„μ μœΌλ‘œ ν›„μ²˜λ¦¬λ₯Ό ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

golang.org/x/sync/errgroup.ErrGroup

errgroup은 sync.WaitGroup을 ν™•μž₯ν•œ νŒ¨ν‚€μ§€μž…λ‹ˆλ‹€. errgroup은 sync.WaitGroupκ³Ό 달리, κ³ λ£¨ν‹΄μ˜ μž‘μ—… 쀑 ν•˜λ‚˜λΌλ„ μ—λŸ¬κ°€ λ°œμƒν•˜λ©΄ λͺ¨λ“  고루틴을 μ·¨μ†Œν•˜κ³  μ—λŸ¬λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "context"
 5    "fmt"
 6    "golang.org/x/sync/errgroup"
 7)
 8
 9func main() {
10    g, ctx := errgroup.WithContext(context.Background())
11    _ = ctx
12
13    for i := 0; i < 10; i++ {
14        i := i
15        g.Go(func() error {
16            if i == 5 {
17                return fmt.Errorf("error")
18            }
19            return nil
20        })
21    }
22
23    if err := g.Wait(); err != nil {
24        fmt.Println(err)
25    }
26}

μœ„ μ½”λ“œλŠ” errgroup을 μ‚¬μš©ν•˜μ—¬ 10개의 고루틴을 μƒμ„±ν•˜κ³ , 5번째 κ³ λ£¨ν‹΄μ—μ„œ μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚€λŠ” μ½”λ“œμž…λ‹ˆλ‹€. μ˜λ„μ μœΌλ‘œ λ‹€μ„―λ²ˆμ§Έ κ³ λ£¨ν‹΄μ—μ„œ μ—λŸ¬λ₯Ό λ°œμƒμ‹œμΌœ, μ—λŸ¬κ°€ λ°œμƒν•˜λŠ” 경우λ₯Ό λ³΄μ—¬λ“œλ ΈμŠ΅λ‹ˆλ‹€. λ‹€λ§Œ μ‹€μ œλ‘œ μ‚¬μš©ν•  λ•Œμ—λŠ” errgroup을 μ‚¬μš©ν•˜μ—¬ 고루틴을 μƒμ„±ν•˜κ³ , 각 κ³ λ£¨ν‹΄μ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•˜λŠ” κ²½μš°μ— λŒ€ν•΄ λ‹€μ–‘ν•œ ν›„μ²˜λ¦¬λ₯Ό μ§„ν–‰ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.

once

ν•œ 번만 μ‹€ν–‰λ˜μ–΄μ•Ό ν•˜λŠ” μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” λ„κ΅¬μž…λ‹ˆλ‹€. μ•„λž˜ μƒμ„±μžλ₯Ό 톡해 κ΄€λ ¨ μ½”λ“œλ₯Ό μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

1func OnceFunc(f func()) func()
2func OnceValue[T any](f func() T) func() T
3func OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)

OnceFunc

OnceFuncλŠ” λ‹¨μˆœνžˆ ν•΄λ‹Ή ν•¨μˆ˜κ°€ 전체에 걸쳐 λ”± ν•œλ²ˆλ§Œ 싀행될 수 있게 ν•΄μ€λ‹ˆλ‹€.

 1package main
 2
 3import "sync"
 4
 5func main() {
 6    once := sync.OnceFunc(func() {
 7        println("Hello, World!")
 8    })
 9
10    once()
11    once()
12    once()
13    once()
14    once()
15}

μœ„ μ½”λ“œλŠ” sync.OnceFunc을 μ‚¬μš©ν•˜μ—¬ Hello, World!λ₯Ό 좜λ ₯ν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” sync.OnceFunc을 μ‚¬μš©ν•˜μ—¬ once ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³ , once ν•¨μˆ˜λ₯Ό μ—¬λŸ¬ 번 ν˜ΈμΆœν•΄λ„ Hello, World!κ°€ ν•œ 번만 좜λ ₯λ©λ‹ˆλ‹€.

OnceValue

OnceValueλŠ” λ‹¨μˆœνžˆ ν•΄λ‹Ή ν•¨μˆ˜κ°€ 전체에 걸쳐 λ”± ν•œλ²ˆλ§Œ μ‹€ν–‰λ˜λŠ” 것이 μ•„λ‹ˆλΌ, ν•΄λ‹Ή ν•¨μˆ˜μ˜ λ°˜ν™˜κ°’μ„ μ €μž₯ν•˜μ—¬ λ‹€μ‹œ ν˜ΈμΆœν•  λ•Œ μ €μž₯된 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

 1package main
 2
 3import "sync"
 4
 5func main() {
 6    c := 0
 7    once := sync.OnceValue(func() int {
 8        c += 1
 9        return c
10    })
11
12    println(once())
13    println(once())
14    println(once())
15    println(once())
16    println(once())
17}

μœ„ μ½”λ“œλŠ” sync.OnceValueλ₯Ό μ‚¬μš©ν•˜μ—¬ c λ³€μˆ˜λ₯Ό 1μ”© μ¦κ°€μ‹œν‚€λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” sync.OnceValueλ₯Ό μ‚¬μš©ν•˜μ—¬ once ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³ , once ν•¨μˆ˜λ₯Ό μ—¬λŸ¬ 번 ν˜ΈμΆœν•΄λ„ c λ³€μˆ˜κ°€ ν•œ 번만 μ¦κ°€ν•œ 1을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

OnceValues

OnceValuesλŠ” OnceValue와 λ™μΌν•˜κ²Œ μž‘λ™ν•˜μ§€λ§Œ, μ—¬λŸ¬ 값을 λ°˜ν™˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 1package main
 2
 3import "sync"
 4
 5func main() {
 6    c := 0
 7    once := sync.OnceValues(func() (int, int) {
 8        c += 1
 9        return c, c
10    })
11
12    a, b := once()
13    println(a, b)
14    a, b = once()
15    println(a, b)
16    a, b = once()
17    println(a, b)
18    a, b = once()
19    println(a, b)
20    a, b = once()
21    println(a, b)
22}

μœ„ μ½”λ“œλŠ” sync.OnceValuesλ₯Ό μ‚¬μš©ν•˜μ—¬ c λ³€μˆ˜λ₯Ό 1μ”© μ¦κ°€μ‹œν‚€λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” sync.OnceValuesλ₯Ό μ‚¬μš©ν•˜μ—¬ once ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κ³ , once ν•¨μˆ˜λ₯Ό μ—¬λŸ¬ 번 ν˜ΈμΆœν•΄λ„ c λ³€μˆ˜κ°€ ν•œ 번만 μ¦κ°€ν•œ 1을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

atomic

atomic νŒ¨ν‚€μ§€λŠ” μ›μžμ  연산을 μ œκ³΅ν•˜λŠ” νŒ¨ν‚€μ§€μž…λ‹ˆλ‹€. atomic νŒ¨ν‚€μ§€λŠ” Add, CompareAndSwap, Load, Store, Swap λ“±μ˜ λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•˜μ§€λ§Œ, μ΅œκ·Όμ—λŠ” Int64, Uint64, Pointer λ“±μ˜ νƒ€μž… μ‚¬μš©μ„ ꢌμž₯ν•©λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "sync"
 5    "sync/atomic"
 6)
 7
 8func main() {
 9    wg := sync.WaitGroup{}
10    c := atomic.Int64{}
11
12    for i := 0; i < 100 ; i++ {
13        wg.Add(1)
14        go func() {
15            defer wg.Done()
16            c.Add(1)
17        }()
18    }
19
20    wg.Wait()
21    println(c.Load())
22}

μ•„κΉŒ μ“°μ˜€λ˜ μ˜ˆμ œμž…λ‹ˆλ‹€. atomic.Int64 νƒ€μž…μ„ μ‚¬μš©ν•˜μ—¬ c λ³€μˆ˜λ₯Ό μ›μžμ μœΌλ‘œ μ¦κ°€μ‹œν‚€λŠ” μ½”λ“œμž…λ‹ˆλ‹€. Add λ©”μ„œλ“œμ™€ Load λ©”μ„œλ“œλ‘œ μ›μžμ μœΌλ‘œ λ³€μˆ˜λ₯Ό μ¦κ°€μ‹œν‚€κ³ , λ³€μˆ˜λ₯Ό μ½μ–΄μ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ Store λ©”μ„œλ“œλ‘œ 값을 μ €μž₯ν•˜κ³ , Swap λ©”μ„œλ“œλ‘œ 값을 κ΅μ²΄ν•˜λ©°, CompareAndSwap λ©”μ„œλ“œλ‘œ 값을 비ꡐ ν›„ μ ν•©ν•˜λ©΄ ꡐ체할 수 μžˆμŠ΅λ‹ˆλ‹€.

cond

sync.Cond

cond νŒ¨ν‚€μ§€λŠ” 쑰건 λ³€μˆ˜λ₯Ό μ œκ³΅ν•˜λŠ” νŒ¨ν‚€μ§€μž…λ‹ˆλ‹€. cond νŒ¨ν‚€μ§€λŠ” sync.Cond둜 생성할 수 있으며, Wait, Signal, Broadcast λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "sync"
 5)
 6
 7func main() {
 8    c := sync.NewCond(&sync.Mutex{})
 9    ready := false
10
11    go func() {
12        // Acquisisce il lock prima di modificare la condizione
13        c.L.Lock()
14        ready = true
15        // Segnala che la condizione Γ¨ stata soddisfatta
16        c.Signal()
17        c.L.Unlock()
18    }()
19
20    // Acquisisce il lock prima di attendere la condizione
21    c.L.Lock()
22    // Attende finchΓ© la condizione 'ready' non diventa vera
23    for !ready {
24        c.Wait()
25    }
26    c.L.Unlock()
27
28    println("Ready!")
29}

μœ„ μ½”λ“œλŠ” sync.Condλ₯Ό μ‚¬μš©ν•˜μ—¬ ready λ³€μˆ˜κ°€ trueκ°€ 될 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” sync.Condλ₯Ό μ‚¬μš©ν•˜μ—¬ ready λ³€μˆ˜κ°€ trueκ°€ 될 λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦° ν›„, Ready!λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€. μ΄λŸ¬ν•œ λ°©μ‹μœΌλ‘œ sync.Condλ₯Ό μ‚¬μš©ν•˜λ©΄, μ—¬λŸ¬ 고루틴이 λ™μ‹œμ— νŠΉμ • 쑰건을 λ§Œμ‘±ν•  λ•ŒκΉŒμ§€ κΈ°λ‹€λ¦¬κ²Œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

이λ₯Ό ν™œμš©ν•˜μ—¬ κ°„λ‹¨ν•œ queueλ₯Ό κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 1package queue
 2
 3import (
 4    "sync"
 5    "sync/atomic"
 6)
 7
 8type Node[T any] struct {
 9    Value T
10    Next  *Node[T]
11}
12
13type Queue[T any] struct {
14    sync.Mutex
15    Cond *sync.Cond
16    Head *Node[T]
17    Tail *Node[T]
18    Len  int
19}
20
21func New[T any]() *Queue[T] {
22    q := &Queue[T]{}
23    // Inizializza Cond con il Mutex della coda
24    q.Cond = sync.NewCond(&q.Mutex)
25    return q
26}
27
28func (q *Queue[T]) Push(value T) {
29    q.Lock()
30    defer q.Unlock()
31
32    node := &Node[T]{Value: value}
33    if q.Len == 0 {
34        q.Head = node
35        q.Tail = node
36    } else {
37        q.Tail.Next = node
38        q.Tail = node
39    }
40    q.Len++
41    // Segnala a un goroutine in attesa che un elemento Γ¨ stato aggiunto
42    q.Cond.Signal()
43}
44
45func (q *Queue[T]) Pop() T {
46    q.Lock()
47    defer q.Unlock()
48
49    // Attende finchΓ© la coda non ha elementi
50    for q.Len == 0 {
51        q.Cond.Wait()
52    }
53
54    node := q.Head
55    q.Head = q.Head.Next
56    q.Len--
57    return node.Value
58}

μ΄λ ‡κ²Œ sync.Condλ₯Ό ν™œμš©ν•˜λ©΄, spin-lock으둜 λ§Žμ€ CPU μ‚¬μš©λŸ‰μ„ μ‚¬μš©ν•˜λŠ” λŒ€μ‹ μ— 효율적으둜 λŒ€κΈ°ν•˜κ³ , 쑰건이 만쑱되면 λ‹€μ‹œ λ™μž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

semaphore

golang.org/x/sync/semaphore.Semaphore

semaphore νŒ¨ν‚€μ§€λŠ” μ„Έλ§ˆν¬μ–΄λ₯Ό μ œκ³΅ν•˜λŠ” νŒ¨ν‚€μ§€μž…λ‹ˆλ‹€. semaphore νŒ¨ν‚€μ§€λŠ” golang.org/x/sync/semaphore.Semaphore둜 생성할 수 있으며, Acquire, Release, TryAcquire λ©”μ†Œλ“œλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€.

 1package main
 2
 3import (
 4    "context"
 5    "fmt"
 6    "golang.org/x/sync/semaphore"
 7)
 8
 9func main() {
10    // Crea un nuovo semaforo pesato con una capacitΓ  massima di 1
11    s := semaphore.NewWeighted(1)
12
13    // Tenta di acquisire un "peso" di 1 dal semaforo
14    if s.TryAcquire(1) {
15        fmt.Println("Acquired!")
16    } else {
17        fmt.Println("Not Acquired!")
18    }
19
20    // Rilascia un "peso" di 1 al semaforo
21    s.Release(1)
22}

μœ„ μ½”λ“œλŠ” semaphoreλ₯Ό μ‚¬μš©ν•˜μ—¬ μ„Έλ§ˆν¬μ–΄λ₯Ό μƒμ„±ν•˜κ³ , μ„Έλ§ˆν¬μ–΄λ₯Ό μ‚¬μš©ν•˜μ—¬ Acquire λ©”μ†Œλ“œλ‘œ μ„Έλ§ˆν¬μ–΄λ₯Ό νšλ“ν•˜κ³ , Release λ©”μ†Œλ“œλ‘œ μ„Έλ§ˆν¬μ–΄λ₯Ό ν•΄μ œν•˜λŠ” μ½”λ“œμž…λ‹ˆλ‹€. 이 μ½”λ“œμ—μ„œλŠ” semaphoreλ₯Ό μ‚¬μš©ν•˜μ—¬ μ„Έλ§ˆν¬μ–΄λ₯Ό νšλ“ν•˜κ³  ν•΄μ œν•˜λŠ” 방법을 λ³΄μ—¬λ“œλ ΈμŠ΅λ‹ˆλ‹€.

마치며

기본적인 λ‚΄μš©μ€ μ—¬κΈ°κΉŒμ§€λ§Œ 있으면 될 κ²ƒκ°™μŠ΅λ‹ˆλ‹€. 이 μ•„ν‹°ν΄μ˜ λ‚΄μš©μ„ ν† λŒ€λ‘œ, μ—¬λŸ¬λΆ„λ“€μ΄ 고루틴을 μ‚¬μš©ν•˜μ—¬ λ™μ‹œμ„±μ„ κ΄€λ¦¬ν•˜λŠ” 방법을 μ΄ν•΄ν•˜κ³ , μ‹€μ œλ‘œ μ‚¬μš©ν•  수 있게 λ˜μ…¨μœΌλ©΄ μ’‹κ² μŠ΅λ‹ˆλ‹€. 이 아티클이 μ—¬λŸ¬λΆ„λ“€μ—κ²Œ 도움이 λ˜μ—ˆμœΌλ©΄ μ’‹κ² μŠ΅λ‹ˆλ‹€. κ°μ‚¬ν•©λ‹ˆλ‹€.