once.go

概述

sync包中的once.go可以在并发情况下保证自定义方法仅仅被执行一次

原型

Once Struct

type Once struct {
    m sync.Mutex
    done int32
}

Do

func (o *Once) Do(f func())

自己玩玩

package main

import (
    "sync"
    "sync/atomic"
    "fmt"
)

type Once struct {
    m sync.Mutex
    done int32
}

func (o *Once) Do(f func()) {
    if atomic.CompareAndSwapInt32(&o.done, 0, 1) {
        f()
    }
}

func main() {
    var once Once
    var wg sync.WaitGroup
    wg.Add(10000)

    for i := 10000; i > 0; i-- {
        go func() {
            defer wg.Done()
            once.Do(func() {
                fmt.Println("once")
            })
        }()
    }

    wg.Wait()
}

使用atomic包的原语一句话就搞定了,但是Go对于Once的实现并没有做的这么简单

Once.Do

源码

func (o *Once) Do(f func()) {
        // 原子载入
    if atomic.LoadUint32(&o.done) == 1 {
        return
    }
    // 上锁
    o.m.Lock()
    defer o.m.Unlock()
        // 避免o.m.Lock()之前o.done被更改
    if o.done == 0 {
                // 原子存储
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

麻烦,的确实现的好麻烦,还用到了锁,是不是谷歌大神写这个的时候心情不好~哈哈哈,其实不然,因为仔细看go 包的官方文档里面有这么一句话 no call to Do returns until the one call to f returns 翻译过来大致意思就是 - f函数没返回之前,不能再调用Do,好吧,的确,如果在f中调用Do,按照上面我自己的实现是不会执行f里面的Do的,但是使用者却没有感知,这点谷歌大神们可能觉得不太友好吧

你可能感兴趣的:(once.go)