golang源代码阅读,sync系列-Once

aseto · · 1045 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

[csdn链接](https://blog.csdn.net/u014763610/article/details/116564095) # 总结 1. Once的作用为只执行函数一次。Once使用的场景并不多。因为初始化单例,一般是利用init函数,init函数也执行一次,但是init函数里面执行的东西建议是非阻塞性的,否则会影响整体程序的加载,且不利于定位问题,如果阻塞了话。阻塞性的可以使用Once,如配置初始化 2. Once执行的函数是没有入参和返回参数的,所以一般会使用闭包的方式,初始化一些参数 3. 因为Once的实例,一般是定义为全局变量,这样让某个函数只执行一次 4. 跟sync下的Mutex、RWMutex一样,也是初始化后,不能copy,copy后新的也变量,再次做Do,也不会执行 ```golang var once sync.Once once.Do(func() { fmt.Println("first") }) twice := once twice.Do(func() { // 不会再次执行 fmt.Println("second") }) ``` 并且使用go vet main.go会有warning ``` main.go:14:11: assignment copies lock value to twice: sync.Once contains sync.Mutex ``` 5. 函数只执行一次, 即使函数里面发生panic # 使用 ```golang var once sync.One var a int once.Init(func() { a = 1 }) ``` # 源码分析 ## 结构体 ```golang type Once struct { // done indicates whether the action has been performed. // It is first in the struct because it is used in the hot path. // The hot path is inlined at every call site. // Placing done first allows more compact instructions on some architectures (amd64/386), // and fewer instructions (to calculate offset) on other architectures. // done确认是否已经执行过,标记位,done放在地位,在某些架构,能让指令更加紧凑(具体不太明白) done uint32 // 用作确保Do的函数只执行一次 m Mutex } ``` ## Do函数 1. 使用下面的方式实现的once action,虽然可以让函数f只执行一次,但是不能保证f是否已经完成,与Once的语义不一样, Once要求的是f执行完了,才能进入下一个Do ```golang if atomic.CompareAndSwapUint32(&o.done, 0, 1) { f() } ``` 3. 函数f执行过程发生panic,外层会捕获,并且将done设置为1,保证函数f只执行一次,不管什么情况 ```golang func (o *Once) Do(f func()) { // Note: Here is an incorrect implementation of Do: // // if atomic.CompareAndSwapUint32(&o.done, 0, 1) { // f() // } // // Do guarantees that when it returns, f has finished. // This implementation would not implement that guarantee: // given two simultaneous calls, the winner of the cas would // call f, and the second would return immediately, without // waiting for the first's call to f to complete. // This is why the slow path falls back to a mutex, and why // the atomic.StoreUint32 must be delayed until after f returns. // 只有done是0的时候才能进入doSlow,可能有多个goroutine进入doSlow if atomic.LoadUint32(&o.done) == 0 { // Outlined slow-path to allow inlining of the fast-path. o.doSlow(f) } } ``` ```golang func (o *Once) doSlow(f func()) { // 调用Lock,保证同时只有一个goroutine进入下面的代码 o.m.Lock() // 整体执行完后,Unlock defer o.m.Unlock() // 一定要再次检查done,否则可能另个一goroutine进入执行f if o.done == 0 { // defer 表明如果函数f执行过程中出现panic,也会将donw置为1 defer atomic.StoreUint32(&o.done, 1) f() } } ```

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1045 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传