sync:二. 延迟初始化(once)

sync.Once 是 Go 标准库提供的使函数只执行一次的实现。作用与 init 函数类似,但有区别。在某些情况下预先初始化一个变量会增加函数的启动延迟,如果实际执行时可能用不上这个变量,那么初始化就是非必须的。sync.Once很好的解决了这个问题,Once可以在任意的位置调用,并且只会执行一次,并发场景下是线程安全的。

Do方法

Once只提供了一个 func (o *Once) Do(f func()) 方法。Do方法调用一个函数,这里有三种情况:

  1. 如果函数没有被调用则调用它。
  2. 如果函数已经被调用完成则不调用.

  3. 如果函数正在被调用(未完成),则等待函数调用完成再返回。

func main() {
    var once sync.Once
    onceBody := func(){
        fmt.Println("Only once")
    }
    done := make(chan bool)
    for i:=0; i<10; i++ { go func() once.do(oncebody) done <- true }() } for i:="0;" i<10; <-done 结果: only once < code></10;>

Once源码

Once结构

// Once &#x662F;&#x4E00;&#x4E2A;&#x5C06;&#x6267;&#x884C;&#x4E00;&#x4E2A;&#x52A8;&#x4F5C;&#x7684;&#x5BF9;&#x8C61;&#x3002;
type Once struct {
    // done &#x6307;&#x793A;&#x64CD;&#x4F5C;&#x662F;&#x5426;&#x5DF2;&#x6267;&#x884C;&#x3002;&#x5B83;&#x5728;&#x7ED3;&#x6784;&#x4E2D;&#x662F;&#x7B2C;&#x4E00;&#x4E2A;&#xFF0C;&#x56E0;&#x4E3A;&#x5B83;&#x7528;&#x4E8E;&#x70ED;&#x8DEF;&#x5F84;&#x3002;&#x70ED;&#x8DEF;&#x5F84;&#x5728;&#x6BCF;&#x4E2A;&#x8C03;&#x7528;&#x7AD9;&#x70B9;&#x5185;&#x8054;&#x3002;
    // &#x9996;&#x5148;&#x653E;&#x7F6E;&#x5141;&#x8BB8;&#x5728;&#x67D0;&#x4E9B;&#x67B6;&#x6784;&#xFF08;amd64x86&#xFF09;&#x4E0A;&#x4F7F;&#x7528;&#x66F4;&#x7D27;&#x51D1;&#x7684;&#x6307;&#x4EE4;&#xFF0C;&#x800C;&#x5728;&#x5176;&#x4ED6;&#x67B6;&#x6784;&#x4E0A;&#x4F7F;&#x7528;&#x66F4;&#x5C11;&#x7684;&#x6307;&#x4EE4;&#xFF08;&#x8BA1;&#x7B97;&#x504F;&#x79FB;&#x91CF;&#xFF09;&#x3002;
    done uint32
    m    Mutex
}

Once结构由两个字段一个 uint32类型的done来标志操作是否执行。这个字段放在第一个的原因是可以使用偏移量的形式来获取。然后就是一个互斥锁。

Do实现

func (o *Once) Do(f func()) {
    // &#x6CE8;&#x610F;&#xFF1A;&#x8FD9;&#x662F; Do &#x7684;&#x9519;&#x8BEF;&#x5B9E;&#x73B0;&#xFF1A; if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() }
    // Do &#x4FDD;&#x8BC1;&#x5F53;&#x5B83;&#x8FD4;&#x56DE;&#x65F6;&#xFF0C; f &#x5DF2;&#x7ECF;&#x5B8C;&#x6210;&#x3002;&#x8FD9;&#x4E2A;&#x5B9E;&#x73B0;&#x4E0D;&#x4F1A;&#x5B9E;&#x73B0;&#x8FD9;&#x4E2A;&#x4FDD;&#x8BC1;&#xFF1A;
    // &#x7ED9;&#x5B9A;&#x4E24;&#x4E2A;&#x540C;&#x65F6;&#x8C03;&#x7528;&#xFF0C;cas &#x7684;&#x83B7;&#x80DC;&#x8005;&#x5C06;&#x8C03;&#x7528; f&#xFF0C;&#x7B2C;&#x4E8C;&#x4E2A;&#x5C06;&#x7ACB;&#x5373;&#x8FD4;&#x56DE;&#xFF0C;
    // &#x800C;&#x4E0D;&#x7B49;&#x5F85;&#x7B2C;&#x4E00;&#x4E2A;&#x5BF9; f &#x7684;&#x8C03;&#x7528;&#x5B8C;&#x6210;&#x3002;&#x8FD9;&#x5C31;&#x662F;&#x4E3A;&#x4EC0;&#x4E48;&#x6162;&#x901F;&#x8DEF;&#x5F84;&#x56DE;&#x9000;&#x5230;&#x4E92;&#x65A5;&#x4F53;&#xFF0C;
    // &#x4EE5;&#x53CA;&#x4E3A;&#x4EC0;&#x4E48; atomic.StoreUint32 &#x5FC5;&#x987B;&#x5EF6;&#x8FDF;&#x5230; f &#x8FD4;&#x56DE;&#x4E4B;&#x540E;&#x3002;
    if atomic.LoadUint32(&o.done) == 0 {
        // &#x6162;&#x901F;&#x8DEF;&#x5F84;&#x4EE5;&#x5141;&#x8BB8;&#x5185;&#x8054;&#x5FEB;&#x901F;&#x8DEF;&#x5F84;&#x3002;
        o.doSlow(f)
    }
}

func (o *Once) doSlow(f func()) {
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

// &#x5FEB;&#x901F;&#x8DEF;&#x5F84;&#x662F;&#x6307;&#x505A;&#x529F;&#x8F83;&#x5C11;&#x7684;&#x8DEF;&#x5F84;&#xFF0C;&#x800C;&#x6162;&#x901F;&#x8DEF;&#x5F84;&#x662F;&#x6307;&#x505A;&#x529F;&#x8F83;&#x591A;&#x7684;&#x8DEF;&#x5F84;&#x3002;

这里可以看到如果第二次调用函数时发现第一次调用还未调用结束,会在doSlow中等待解锁,这里用一个互斥锁避免第一次调用未结束,第二次调用就返回的情况。

Original: https://www.cnblogs.com/ourongxin/p/16000406.html
Author: EthanWell
Title: sync:二. 延迟初始化(once)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516396/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球