• 用CAS操作实现Go标准库中的Once


    Go标准库中提供了Sync.Once来实现“只执行一次”的功能。学习了一下源代码,里面用的是经典的双重检查的模式:

    // Once is an object that will perform exactly one action.
    type Once struct {
    	m    Mutex
    	done uint32
    }
    
    func (o *Once) Do(f func()) {
    	if atomic.LoadUint32(&o.done) == 1 {
    		return
    	}
    	// Slow-path.
    	o.m.Lock()
    	defer o.m.Unlock()
    	if o.done == 0 {
    		f()
    		atomic.StoreUint32(&o.done, 1)
    	}
    }
    

    我觉得这里也可以用Go的原子操作来实现“只执行一次”,代码更简单,而且比起用Mutex的开销要更小一些:

    type Once struct {
    	done int32
    }
    
    func (o *Once) Do(f func()) {
    	if atomic.LoadInt32(&o.done) == 1 {
    		return
    	}
    	// Slow-path.
    	if atomic.CompareAndSwapInt32(&o.done, 0, 1) {
    		f()
    	}
    }
    

    熟悉Java内存模型的程序员可能会留意到上面的CAS操作中传递的是变量地址。如果在Java中,这样的变量是需要volatile来保证线程之间的可见性的,而Golang并没有volatile,Golang的内存模型的happen-after规则也没有提到atomic操作。但从Golang标准库的相关源码来看,Golang的atomic操作应该是可以满足可见性要求的。

    从下面的测试上看,这样实现没有什么并发的bug。

    	runtime.GOMAXPROCS(1000)
    	n := 100000
    	wg := new(sync.WaitGroup)
    	wg.Add(n)
    	
    	a := int32(0)
    	for i := 0; i < n; i++{
    		go func(){
    			if atomic.CompareAndSwapInt32(&a, 0, 1) {
    				fmt.Println("Change a to 1")
    			}
    			wg.Done()
    		}()
    	}
    	wg.Wait()
    
    	fmt.Println("Test is done")
    

    Go的内存模型文档中对atomic包并未提起,如果参考Java的来看

  • 相关阅读:
    rabbitmq系统学习(三)集群架构
    rabbitmq系统学习(二)
    rabbitmq系统学习(一)
    itext实现pdf自动定位合同签订
    itext7知识点研究(PDF编辑)
    itext实现合同尾部签章部分自动添加,定位签名
    ELK实战(Springboot日志输出查找)
    [Wireshark]_002_玩转数据包
    [Wireshark]_001_入门
    [Objective-C] 014_Objective-C 代码规范指南
  • 原文地址:https://www.cnblogs.com/liaofan/p/4010436.html
Copyright © 2020-2023  润新知