Go语言中管理状态的主要机制是通过通道进行通信。在过去的文章中我们已经看到了这一点,例如工人池。 还有一些其他选项用于管理状态。 这里我们将使用sync/atomic
包来实现由多个goroutine
访问的原子计数器。
使用一个无符号整数表示计数器(正数)。
为了模拟并发更新,将启动50
个goroutine
,每个增量计数器大约是1
毫秒。
为了原子地递增计数器,这里使用AddUint64()
函数,在ops
计数器的内存地址上使用&
语法。
在增量之间等待一秒,允许一些操作累积。
为了安全地使用计数器,同时它仍然被其他goroutine
更新,通过LoadUint64
提取一个当前值的副本到opsFinal
。 如上所述,需要将获取值的内存地址&ops
给这个函数。
运行程序显示执行了大约40,000
次操作。
所有的示例代码,都放在
F:\worksp\golang
目录下。安装Go编程环境请参考:http://www.yiibai.com/go/go_environment.html
atomic-counters.go
的完整代码如下所示 -
package main
import "fmt"
import "time"
import "sync/atomic"
func main() {
// We'll use an unsigned integer to represent our
// (always-positive) counter.
var ops uint64 = 0
// To simulate concurrent updates, we'll start 50
// goroutines that each increment the counter about
// once a millisecond.
for i := 0; i < 50; i++ {
go func() {
for {
// To atomically increment the counter we
// use `AddUint64`, giving it the memory
// address of our `ops` counter with the
// `&` syntax.
atomic.AddUint64(&ops, 1)
// Wait a bit between increments.
time.Sleep(time.Millisecond)
}
}()
}
// Wait a second to allow some ops to accumulate.
time.Sleep(time.Second)
// In order to safely use the counter while it's still
// being updated by other goroutines, we extract a
// copy of the current value into `opsFinal` via
// `LoadUint64`. As above we need to give this
// function the memory address `&ops` from which to
// fetch the value.
opsFinal := atomic.LoadUint64(&ops)
fmt.Println("ops:", opsFinal)
}
执行上面代码,将得到以下输出结果 -
F:\worksp\golang>go run atomic-counters.go
ops: 41360