Go言語の並行処理入門

Goゴルーチン

Goのゴルーチンはグリーンスレッドで、goキーワードで起動します。

package main

import (
    "fmt"
    "time"
)

func printMessage(msg string) {
    for i := 0; i < 5; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(msg)
    }
}

func main() {
    go printMessage("world")
    printMessage("hello")
}

チャネル

// 同期チャネルの基本操作
ch := make(chan int)
ch <- 10 // 送信
val := <-ch // 受信

// ゴルーチンとチャネルの連携
func calculateSum(numbers []int, result chan int) {
    total := 0
    for _, v := range numbers {
        total += v
    }
    result <- total
}

data := []int{3, -2, 5, 7, -4, 8}
resultChan := make(chan int)

go calculateSum(data[:3], resultChan)
go calculateSum(data[3:], resultChan)

a, b := <-resultChan, <-resultChan
fmt.Println(a, b, a+b) // -4 15 11

// バッファ付きチャネル
buffered := make(chan int, 3)
buffered <- 100
buffered <- 200
fmt.Println(<-buffered)
fmt.Println(<-buffered)
buffered <- 300

チャネルのイテレーションとクローズ

func generateSequence(n int, out chan int) {
    a, b := 0, 1
    for i := 0; i < n; i++ {
        out <- a
        a, b = b, a+b
    }
    close(out)
}

seqChan := make(chan int, 8)
go generateSequence(cap(seqChan), seqChan)

for num := range seqChan {
    fmt.Println(num)
}

チャネル選択

func sequenceGenerator(output, exit chan int) {
    a, b := 0, 1
    for {
        select {
        case output <- a:
            a, b = b, a+b
        case <-exit:
            fmt.Println("シーケンス終了")
            return
        }
    }
}

output := make(chan int)
exitSignal := make(chan int)

go func() {
    for i := 0; i < 8; i++ {
        fmt.Println(<-output)
    }
    exitSignal <- 0
}()

sequenceGenerator(output, exitSignal)

// タイムアウト処理
tickTimer := time.Tick(150 * time.Millisecond)
shutdown := time.After(1 * time.Second)

for {
    select {
    case <-tickTimer:
        fmt.Println("イベント発生")
    case <-shutdown:
        fmt.Println("タイムアウト")
        return
    default:
        fmt.Println("...")
        time.Sleep(75 * time.Millisecond)
    }
}

排他制御

type ThreadSafeCounter struct {
    values map[string]int
    lock   sync.Mutex
}

func (c *ThreadSafeCounter) Increment(key string) {
    c.lock.Lock()
    c.values[key]++
    c.lock.Unlock()
}

func (c *ThreadSafeCounter) Get(key string) int {
    c.lock.Lock()
    defer c.lock.Unlock()
    return c.values[key]
}

counter := &ThreadSafeCounter{values: make(map[string]int)}
for i := 0; i < 500; i++ {
    go counter.Increment("sample")
}
time.Sleep(500 * time.Millisecond)
fmt.Println(counter.Get("sample")) // 500

ゴルーチン同期

func executeTask(id int, group *sync.WaitGroup) {
    fmt.Printf("タスク%d開始\n", id)
    time.Sleep(1 * time.Second)
    fmt.Printf("タスク%d完了\n", id)
    group.Done()
}

var taskGroup sync.WaitGroup
for i := 1; i <= 3; i++ {
    taskGroup.Add(1)
    go executeTask(i, &taskGroup)
}
taskGroup.Wait()
fmt.Println("すべてのタスクが終了しました")

タグ: goroutines Channels concurrency sync

7月16日 23:12 投稿