1. // 多次调用仅执行一次指定的函数 f
    2. func (o *Once) Do(f func())

    例的fooOnce函数只执行一次打印。

    1. func fooOnce(){
    2. fmt.Println("只会执行一次")
    3. }
    4. func main() {
    5. var once sync.Once
    6. done := make(chan int)
    7. for i := 0; i < 10; i++ {
    8. go func(index int) {
    9. once.Do(fooOnce)
    10. done <- index
    11. }(i)
    12. }
    13. for i := 0; i < 10; i++ {
    14. fmt.Println("接手到的数据",<-done)
    15. }
    16. }

    单例模式

    1. package main
    2. import (
    3. "fmt"
    4. "sync"
    5. )
    6. type Singleton struct {
    7. }
    8. var singleInstance *Singleton
    9. var once sync.Once
    10. func GetSingletonObj() *Singleton {
    11. once.Do(func() {
    12. fmt.Println("Create object")
    13. singleInstance = new(Singleton)
    14. })
    15. return singleInstance
    16. }
    17. func main() {
    18. var wg sync.WaitGroup
    19. for i := 0; i < 10; i++ {
    20. wg.Add(1)
    21. go func() {
    22. obj := GetSingletonObj()
    23. fmt.Printf("%p\n", obj)
    24. wg.Done()
    25. }()
    26. }
    27. wg.Wait()
    28. }

    源码

    1. package sync
    2. import (
    3. "sync/atomic"
    4. )
    5. // Once is an object that will perform exactly one action.
    6. type Once struct {
    7. // done indicates whether the action has been performed.
    8. // It is first in the struct because it is used in the hot path.
    9. // The hot path is inlined at every call site.
    10. // Placing done first allows more compact instructions on some architectures (amd64/x86),
    11. // and fewer instructions (to calculate offset) on other architectures.
    12. done uint32
    13. m Mutex
    14. }
    15. // Do calls the function f if and only if Do is being called for the
    16. // first time for this instance of Once. In other words, given
    17. // var once Once
    18. // if once.Do(f) is called multiple times, only the first call will invoke f,
    19. // even if f has a different value in each invocation. A new instance of
    20. // Once is required for each function to execute.
    21. //
    22. // Do is intended for initialization that must be run exactly once. Since f
    23. // is niladic, it may be necessary to use a function literal to capture the
    24. // arguments to a function to be invoked by Do:
    25. // config.once.Do(func() { config.init(filename) })
    26. //
    27. // Because no call to Do returns until the one call to f returns, if f causes
    28. // Do to be called, it will deadlock.
    29. //
    30. // If f panics, Do considers it to have returned; future calls of Do return
    31. // without calling f.
    32. //
    33. func (o *Once) Do(f func()) {
    34. // Note: Here is an incorrect implementation of Do:
    35. //
    36. // if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
    37. // f()
    38. // }
    39. //
    40. // Do guarantees that when it returns, f has finished.
    41. // This implementation would not implement that guarantee:
    42. // given two simultaneous calls, the winner of the cas would
    43. // call f, and the second would return immediately, without
    44. // waiting for the first's call to f to complete.
    45. // This is why the slow path falls back to a mutex, and why
    46. // the atomic.StoreUint32 must be delayed until after f returns.
    47. if atomic.LoadUint32(&o.done) == 0 {
    48. // Outlined slow-path to allow inlining of the fast-path.
    49. o.doSlow(f)
    50. }
    51. }
    52. func (o *Once) doSlow(f func()) {
    53. o.m.Lock()
    54. defer o.m.Unlock()
    55. if o.done == 0 {
    56. defer atomic.StoreUint32(&o.done, 1)
    57. f()
    58. }
    59. }