いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-591 (Go 1.25でgo vet コマンドに sync.WaitGroup 用のチェックが追加)(Go 1.25 rc1)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。前回の続きです。

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

Go 1.25 のドラフトリリースノートは以下。

tip.golang.org

上記を見ると、Go 1.25 で go vet コマンドに sync.WaitGroup 向けのチェックが追加されるみたいですね。

waitgroup, which reports misplaced calls to sync.WaitGroup.Add;

ってあるので、sync.WaitGroupの間違った使い方があった場合に警告してくれるとのこと。

では、どんなパターンのときに警告してくれるとかというと

waitgroup package - golang.org/x/tools/go/analysis/passes/waitgroup - Go Packages

にあるみたいに

// WRONG
var wg sync.WaitGroup
go func() {
        wg.Add(1) // "WaitGroup.Add called from inside new goroutine"
        defer wg.Done()
        ...
}()
wg.Wait() // (may return prematurely before new goroutine starts)

って感じで、ゴルーチン内で wg.Add() してしまっているパターンのみみたいですね。

このパターン、コンパイルは当然通るし、実行も出来るのですが正しく待ち合わせが出来ない可能性が発生します。(Waitに入る前に動くことが出来たゴルーチンがいた場合は、その数だけ待機してくれるけど、全ゴルーチン分がWait前にAdd(1)を呼び出せるかどうかは不定。普通は速攻でWaitが呼ばれるので、Addしているものが一つも存在せずに即終了してしまう)

一応、ちゃんと警告されるか試してみました。

試してみた

go version

$ go version
go version go1.25rc1 linux/arm64

main.go

package main

import (
        "fmt"
        "sync"
)

func main() {
        const (
                NumWorkers = 3
        )
        var (
                wg sync.WaitGroup
        )
        for range NumWorkers {
                // 本来はゴルーチン外でAddしないと駄目
                go func() {
                        wg.Add(1) // 間違えてる
                        defer wg.Done()

                        fmt.Println("hello world")
                }()
        }

        wg.Wait()
}

go vet

go vet するとちゃんと警告でますね。

$ go vet .
# app
# [app]
./main.go:18:10: WaitGroup.Add called from inside new goroutine

main.go (2)

正しい形に直して

package main

import (
        "fmt"
        "sync"
)

func main() {
        const (
                NumWorkers = 3
        )
        var (
                wg sync.WaitGroup
        )
        for range NumWorkers {
                wg.Add(1)
                go func() {
                        defer wg.Done()

                        fmt.Println("hello world")
                }()
        }

        wg.Wait()
}

vetすると警告は消えました。

$ go vet .

参考情報

pkg.go.dev

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。