Go
golang

sync.Onceの挙動がなんとなくしか分からなかったので実際に試してみた

sync.Onceとは

syncパッケージの処理はこちらで説明されてたので、ご参考までにどうぞ
https://qiita.com/t-mochizuki/items/80dc959b4221c53f3c40#synconce

実際に試してみた

PatternA

とりあえずsync.Onceを使った場合と使わない場合を織り交ぜてみた

テストコード

package main

import "fmt"
import "sync"

var foo string

func main() {
    var once sync.Once

    once.Do(func() {
        foo = "A"
    })
    once.Do(func() {
        foo = "B"
    })
    fmt.Println(foo)
    foo = "C"
    fmt.Println(foo)
    once.Do(func() {
        foo = "D"
    })
    fmt.Println(foo)
}

https://play.golang.org/p/imWwk2ij84-

結果

A
C
C

PatternB

PatternAで fooD にかわらなかったのが、C が代入されていたからか、そのまえにsync.Onceを使っていたからかを切り分ける

テストコード

package main

import "fmt"
import "sync"

var foo string

func main() {
    var once sync.Once

    foo = "C"
    once.Do(func() {
        foo = "D"
    })
    fmt.Println(foo)
}

https://play.golang.org/p/q2-qO73J7L1

結果

D

PatternC

異なる処理を同じ sync.Once を使った場合にどうなるかを確認

テストコード

package main

import "fmt"
import "sync"

var foo string
var bar string

func main() {
    var once sync.Once

    foo = "NoN"
    bar = "NoN"
    once.Do(func() {
        foo = "A"
    })
    once.Do(func() {
        bar = "B"
    })
    fmt.Printf("foo: %s\n",foo)
    fmt.Printf("bar: %s\n",bar)
}

https://play.golang.org/p/A4Uq4yUUhUe

結果

foo: A
bar: NoN

PatternD

異なる処理を異なる sync.Once を使った場合にどうなるかを確認

テストコード

package main

import "fmt"
import "sync"

var foo string
var bar string

func main() {
    var typea sync.Once
    var typeb sync.Once

    foo = "NoN"
    bar = "NoN"
    typea.Do(func() {
        foo = "A"
    })
    typeb.Do(func() {
        bar = "B"
    })
    fmt.Printf("foo: %s\n",foo)
    fmt.Printf("bar: %s\n",bar)
}

https://play.golang.org/p/VYXifllWJyy

結果

foo: A
bar: B

結論

  • sync.Onceは宣言した変数別に管理される
  • 中の処理が異なっていても関係ない。異なる処理を同じSync.Onceで管理してはいけない

当然といったら当然な結果でした。