once 只需要执行一次

文摘   科技   2023-05-27 15:10   四川  

那一天晚上,我闭上眼,不知什么时候做了一个梦,梦里遇到了一些人,也不知过了多久,在睁开眼的时候,梦已经醒了,才发现那些人已经不会在生活中遇到了。在梦里,我找到了自己失去的东西,后来才发现,自己有过的东西都会以某种方式离开自己,无论是自己愿意的还是不愿意的。即使自己不舍得,慢慢的随着时间的远去,自己能做的最多的也就是远远的看着它。


生活如果是一本书,那我们在读的时候,难免会看到很多重复的内容,重复的生活细节使得我们也会去寻找那些新鲜的,甚至是一次性的东西。在追求的过程中我们认识到生活在自己定的一些目标下,变得不那么单调无趣。比如我们会在本子上写下很多的愿望,去爬一次长城,与陌生人进行交流,骑车去西藏,约一个陌生人看一场电影。


在某个时候,我们会回忆起对那些愿望做的尝试,这种回忆会让人有再次体验的愿望,或者会感慨那是自己人生中唯一的经历。很奇怪,有些东西如果不经常重复,我们对它的记忆会变得越来越模糊,而有的东西,只要发生一次,就会永远记得。


后来想想,这样也许是一种平衡,在现实中存在很短暂的,我们通过记忆来延续弥补现实中的那种短暂。


在go里面,如何实现一个事情,始终只执行一次呢,只执行一次的目的还是在有可能多次执行的情况下。程序里面的循环,往往是用来重复的执行同一个动作。检验一个动作只会执行一次,也可以通过循环的方式来检验。

package main
import ( "fmt" "sync")
func print() { fmt.Println("just once")
}func main() { num := 5 once := sync.Once{} for i := 0; i < num; i++ { once.Do(print) }}


上面虽然循环了多次去执行print,结果是指打印了一次。

Once的结构体如下

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 uint32  m    Mutex}


我们看到了之前讲过的Mutex,也就是锁。

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.
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() }}


为了只让函数执行一次,在正在执行之前,加了一把锁,表示多个协程来执行的时候,只能有一个协程在去执行这个函数,并且在执行完成之后,会通过原子操作去修改done这个状态,表示已经执行一次了,下一次进来的协程看到这个状态是已经执行了,就不会去再次执行。


可以看的出,Do函数执行的函数是一个没有参数的函数,也没有返回值。主要是用在一些初始化的地方,比如在程序里面需要初始化一些配置的参数,程序才可以正常运行。

注意 因为有锁的原因,所以在f函数里面如果去调用Do的话就会发生死锁,相当于里面的协程再次获取另一个协程获得的锁。

golang学习记
分享golang 学习的点滴
 最新文章