Golang atomic.AddUint64() 函数及其示例
Go 语言在并发编程中是一种十分流行的语言,经常需要对变量进行原子性的操作,以避免并发访问时的数据竞争。在 Go 语言中,我们可以使用 atomic 包来进行原子性的操作,其中 atomic.AddUint64() 函数可以用于原子性的增加一个 uint64 类型的变量的值。本文将介绍这个函数的用法及其示例代码。
atomic.AddUint64() 函数的用法
atomic.AddUint64() 函数的原型如下:
func AddUint64(addr *uint64, delta uint64) (new uint64)
其中,addr 参数为一个指向 uint64 类型变量的指针,delta 参数为要增加的值。执行该函数后,会原子性地将 addr 指向的变量的值加上 delta,并返回新的值。这个操作是原子性的,因此不会受到其他 goroutine 的干扰。
下面是一个使用 atomic.AddUint64() 函数的示例代码:
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var num uint64 = 0
for i := 0; i < 1000; i++ {
go func() {
atomic.AddUint64(&num, 1)
}()
}
// 等待所有 goroutine 执行完毕
time.Sleep(time.Second)
fmt.Println(num)
}
在这个示例代码中,我们定义了一个名为 num 的 uint64 类型变量,并使用 atomic.AddUint64() 函数在多个 goroutine 中对其进行原子性的增加。最终,我们会得到 num 的最终值,该值是在多个 goroutine 中进行原子性操作后的结果。
需要注意的是,在使用 atomic.AddUint64() 函数时,第一个参数 addr 必须是一个指针类型的变量的指针,而不能是一个普通的变量。这是因为 atomic 包需要通过指针来修改变量的值。同时,delta 参数必须是一个正整数,不能是负数或者浮点数。
示例代码
接下来,我们将展示 atomic.AddUint64() 函数在实际应用中的示例代码。
示例 1:计数器
下面是一个用于计数的示例程序,它使用 atomic.AddUint64() 函数实现并发安全的计数器:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter uint64
var wg sync.WaitGroup
wg.Add(10000)
for i := 0; i < 10000; i++ {
go func() {
atomic.AddUint64(&counter, 1)
wg.Done()
}()
}
wg.Wait()
fmt.Println(counter)
}
在这个程序中,我们定义了一个名为 counter 的 uint64 类型变量,并使用 atomic.AddUint64() 函数在多个 goroutine 中对其进行原子性的增加。在所有 goroutine 执行完毕后,我们会输出 counter 的最终值。
示例 2:单例模式
下面是一个使用 atomic 包实现的单例模式的示例代码:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type Singleton struct {}
var instance *Singleton
var once uint32
var mtx sync.Mutex
func NewSingleton() *Singleton {
if atomic.LoadUint32(&once) == 1 {
return instance
}
mtx.Lock()
defer mtx.Unlock()
if instance == nil {
instance = &Singleton{}
atomic.StoreUint32(&once, 1)
}
return instance
}
func main() {
singleton := NewSingleton()
fmt.Println(singleton)
}
在这个程序中,我们定义了一个名为 Singleton 的结构体,它表示要保证唯一实例的类型。我们通过将很多函数绑定在它上面实现一个类。但我们希望在程序运行中实现单例模式,因此使用 atomic 包来保证原子性、线程安全。在 NewSingleton() 函数中,使用 atomic.LoadUint32() 函数来检查 once 变量是否被设置为 1,并使用 sync.Mutex 来保护实例 instance 的创建。
结论
使用 atomic 包中的 atomic.AddUint64() 函数可以实现对 uint64 类型变量的原子性增加操作,可以避免并发访问时的数据竞争。在实际应用中,可以将这个函数应用到并发计数器、单例模式等场景中,提高程序的并发性能和安全性。但使用 atomic 包并不是万能的解决方案,需要开发者合理使用,结合其他并发控制工具来保证程序的正确性和性能。