博客
关于我
NSQD对sync.Once的使用及sync.Once探索
阅读量:579 次
发布时间:2019-03-11

本文共 1948 字,大约阅读时间需要 6 分钟。

sync.Once 在 NSQD 启动过程中的应用

在 Go 语言中,sync.Once 是一个用于确保某个操作仅执行一次的互斥原语。它的核心作用是方便开发者在多 goroutine 调用环境下,只初始化一次或执行一次特定操作。


NSQD 启动过程中的 Once 使用

在 NSQD(Non-Blocking Sequential Data)项目中,sync.Once 被用于确保插件(plugin)的加载和初始化只执行一次。这在并发环境下非常重要,因为重复初始化可能导致资源冲突或性能问题。

项目的核心结构体如下:

type program struct {    once sync.Once    nsqd *nsqd.NSQD}

在插件的Stop()方法中,我们通过 sync.Once 来确保对 NSQD 的退出操作只执行一次:

func (p *program) Stop() error {    p.once.Do(func() {        p.nsqd.Exit()    })    return nil}

这样设计不仅保证了退出操作的原子性,还避免了多 goroutine 同时触发退出的情况。


sync.Once 的深入探索

1. sync.Once 的定义与作用

sync.Once 由 Go 标准库提供,仅包含两个字段:done uint32m Mutex。它的主要功能是确保某个操作仅执行一次。Do 方法允许调用者多次调用,但只有第一次调用时传递的函数 f 才会被执行。

其设计目的是为了在并发环境中确保关键初始化操作只运行一次,这在分布式系统中非常重要。

2. sync.Once 的典型使用场景

sync.Once 适用于以下场景:

  • 延迟初始化:确保某个资源在首次访问时被初始化,之后直接使用。
  • 消耗式使用:执行一个“消耗性”操作,只能进行一次。
  • 退出机制:在一些退出流程中,确保只执行一次终止操作。

例如,在 NSQD 项目中,Exit() 方法的调用只能执行一次,确保插件的资源释放得当。

3. sync.Once 的实现原理

sync.Once 的实现基于互斥锁(m Mutex)和双重检查(doSlow 方法):

type Once struct {    done uint32    m Mutex}func (o *Once) Do(f func()) {    if atomic.LoadUint32(&o.done) == 0 {        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()    }}

Do 方法中,atomic.LoadUint32 快速检查 done 是否已设置。如果未设置,则通过 doSlow 方法执行 f

doSlow 方法中,先 acquire 互斥锁,再通过双重检查确保 done 仍未设置。如果是,则执行函数 f 并设置 done。这保证了只有一次执行的原子性。

这种设计避免了并发 goroutine 在没有互斥锁的情况下可能导致的竞态条件。

4. sync.Once 的错误使用方式

尽管 sync.Once 简单,但错误使用可能导致严重后果。根据标准库注释:

  • 如果 f 调用 Do,其中 f 调用 Do,可能导致死锁。
  • 如果 f panic,Do 会认为初始化已完成,后续调用不会重试。

因此,在使用时必须谨慎:

// 错误示例 1:死锁func main() {    var once sync.Once    once.Do(func() {        once.Do(func() {            fmt.Println("初始化中...")        })    })}

这种情况会导致死锁,因为 Do 会在前一个调用完成后立即返回,而第二个调用则在等待 once.Do 返回时被阻塞。


结论

sync.Once 是 Go 语言中处理初始化和单例资源的重要工具。通过其封装的 Do 方法,可以确保某些操作仅执行一次,从而在多 goroutine 环境中维护资源的单例性。在 NSQD 项目中,sync.Once 被用来管理插件的启动与退出,保证系统的稳定性和高效性。

正确使用 sync.Once 需要注意潜在的竞态条件和死锁风险,始终遵循其设计原理:互斥锁 + 双重检查。

转载地址:http://dwmvz.baihongyu.com/

你可能感兴趣的文章
Openlayers实战:绘制多边形,导出CSV文件
查看>>
Openlayers实战:绘制带箭头的线
查看>>
Openlayers实战:自定义放大缩小,显示zoom等级
查看>>
Openlayers实战:自定义版权属性信息
查看>>
Openlayers实战:输入WKT数据,输出GML、Polyline、GeoJSON格式数据
查看>>
Openlayers实战:选择feature,列表滑动,定位到相应的列表位置
查看>>
Openlayers实战:非4326,3857的投影
查看>>
Openlayers高级交互(1/20): 控制功能综合展示(版权、坐标显示、放缩、比例尺、测量等)
查看>>
Openlayers高级交互(10/20):绘制矩形,截取对应部分的地图并保存
查看>>
Openlayers高级交互(11/20):显示带箭头的线段轨迹,箭头居中
查看>>
Openlayers高级交互(12/20):利用高德逆地理编码,点击位置,显示坐标和地址
查看>>
Openlayers高级交互(13/20):选择左右两部分的地图内容,横向卷帘
查看>>
Openlayers高级交互(14/20):汽车移动轨迹动画(开始、暂停、结束)
查看>>
Openlayers高级交互(15/20):显示海量多边形,10ms加载完成
查看>>
Openlayers高级交互(16/20):两个多边形的交集、差集、并集处理
查看>>
Openlayers高级交互(17/20):通过坐标显示多边形,计算出最大幅宽
查看>>
Openlayers高级交互(18/20):根据feature,将图形适配到最可视化窗口
查看>>
Openlayers高级交互(19/20): 地图上点击某处,列表中显示对应位置
查看>>
Openlayers高级交互(2/20):清除所有图层的有效方法
查看>>
Openlayers高级交互(20/20):超级数据聚合,页面不再混乱
查看>>