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的,但是使用者却没有感知,这点谷歌大神们可能觉得不太友好吧

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容