美文网首页golang学习篇章
sync.Once 短小精悍

sync.Once 短小精悍

作者: Best博客 | 来源:发表于2020-09-01 11:55 被阅读0次

sync.Once 源码

package sync

import (
    "sync/atomic"
)
type Once struct {
    done uint32
    m    Mutex
}

func (o *Once) Do(f func()) {
    if atomic.LoadUint32(&o.done) == 0 {
        // Outlined slow-path to allow inlining of the fast-path.
        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()
    }
}

剔除源码注释之后,才这么几行代码,却能发挥巨大的作用,但里面有些小细节还是值得好好推敲的。

适合场景:

  1. 全局变量初始化
  2. 懒汉模式的单例
  3. 服务接受系统级别的kill信号去触发业务代码,比如 kill -15 pid。
  4. 所有只允许执行一次的场景。。

不太适合场景:

  1. 一上来就高并发场景:一上来就高并发场景看源码就会发现逻辑走到 sync.Mutex 里面去控制了,那么这个时候,你倒是不如直接 sync.Mutex 去控制。 不过也就多了个 atomic.LoadUint32 级别的操作呀,所以不足为虑。

细节刨析

以下纯搬运,其实源码注释里面也有

image.png
答:如果不使用 atomic,无法及时观察doSlow对o.done 的修改。 什么叫及时观察? 首先 要明白sync.Once{}.Do() 它是可能被高频的在多核里面并发并行运行的,内存同步可能是不及时的
。假如将 atomic 的操作改为普通的 &o.done==0 与 o.done = 1 语句其实也是会起到内存同步的(go里面内存模型一致性的保障是可以通过同步事件去达到的,这里o.m.lock就是同步事件之一),但这个同步事件是发生在f() 函数执行完毕后去同步的(而且还真得等到f执行完毕,后面说..)假如f执行花了5ms,就意味着5ms放进来的并发请求串行全靠 o.m.lock 去保证了。加不加 atomic 都会有lock给你兜底,只是atomic加了 done一旦变化,里面上层的 if atomic.LoadUint32(&o.done) == 0 { 就判断出来了,而不加还需要unlock触发同步事件,很明显前者更及时。

为啥非要等f() 执行完毕 再通过 o.m.Unlock() 去触发同步事件,达到内存一致性,为啥不把f() 放goroutinue 直接执行 从而更快的触发 同步事件,这样上文的5ms不就不存在了么。

答:其实这样首先你也只能是缩短5ms这个时间,其次还真不能不等f() 没执行完就触发修改done的值并里面触发同步事件,你想撒,f() 明明还没执行完,你就通知其他人 这个函数已经执行过了,这是不对的,万一后面的业务对 f() 执行结果有强依赖。。。 伪高效埋巨坑。 其实这也是为什么没有用 if atomic.CompareAndSwapUint32(&o.done, 0, 1) { 去替换 atomic.LoadUint32(&o.done) == 0 { 的原因了

相关文章

  • sync.Once 短小精悍

    sync.Once 源码 剔除源码注释之后,才这么几行代码,却能发挥巨大的作用,但里面有些小细节还是值得好好推敲的...

  • 深度剖析Golang sync.Once源码

    目录 什么是sync.Once 如何使用sync.Once 源码分析 什么是sync.Once Once 可以用来...

  • sync.Once

    利用 sync.once 实现单例 sync.Once 实现 为什么要用defer 来加计数?不直接在后面执行计数...

  • Go语言——sync.Once分析

    Go语言——sync.Once分析 sync.Once表示只执行一次函数。要做到这点,就需要两点: 计数器,统计函...

  • golang sync.Once源代码阅读--让你的初始化只执行

    sync.Once的用处 sync.Once 主要用途执行只需要执行一次的初始化函数,比如验证器的初始,http....

  • sync.Once

    sync.Once 的使用场景 sync.Once 是 Go 标准库提供的使函数只执行一次的实现,常应用于单例模式...

  • go 中的 sync.Once

    sync.Once 是 Go 标准库提供的使函数只执行一次的实现,常应用于单例模式,sync.Once 仅提供了一...

  • sync.Once

    once结构是这样的 其中只有这么一个方法 作用是保证多个协程只执行某个函数一次为什么不能使用CAS原子操作来替代...

  • golang 系列:sync.Once 讲解

    sync.Once 介绍 之前提到过 Go 的并发辅助对象:WaitGroup[https://mp.weixin...

  • sync.Once分析

    使用场合 在实际生产中,很多时候我们需要执行只有一次的操作(如单例模式)。在golang中,我们可以简单的使用sy...

网友评论

    本文标题:sync.Once 短小精悍

    本文链接:https://www.haomeiwen.com/subject/fottdktx.html