在前面的例子中,我们看到了如何使用原子操作来管理简单的计数器。对于更加复杂的情况,我们可以使用一个互斥锁来在 Go 协程间安全的访问数据。 |
|
package main
|
|
import (
"fmt"
"math/rand"
"runtime"
"sync"
"sync/atomic"
"time"
)
|
|
func main() {
|
|
在我们的例子中, |
var state = make(map[int]int)
|
这里的 |
var mutex = &sync.Mutex{}
|
we'll see later, |
var ops int64 = 0
|
这里我们运行 100 个 Go 协程来重复读取 state。 |
for r := 0; r < 100; r++ {
go func() {
total := 0
for {
|
每次循环读取,我们使用一个键来进行访问, |
key := rand.Intn(5)
mutex.Lock()
total += state[key]
mutex.Unlock()
atomic.AddInt64(&ops, 1)
|
为了确保这个 Go 协程不会在调度中饿死,我们在每次操作后明确的使用 |
runtime.Gosched()
}
}()
}
|
同样的,我们运行 10 个 Go 协程来模拟写入操作,使用和读取相同的模式。 |
for w := 0; w < 10; w++ {
go func() {
for {
key := rand.Intn(5)
val := rand.Intn(100)
mutex.Lock()
state[key] = val
mutex.Unlock()
atomic.AddInt64(&ops, 1)
runtime.Gosched()
}
}()
}
|
让这 10 个 Go 协程对 |
time.Sleep(time.Second)
|
获取并输出最终的操作计数。 |
opsFinal := atomic.LoadInt64(&ops)
fmt.Println("ops:", opsFinal)
|
对 |
mutex.Lock()
fmt.Println("state:", state)
mutex.Unlock()
}
|
运行这个程序,显示我们对已进行了同步的 |
$ go run mutexes.go
ops: 3598302
state: map[1:38 4:98 2:23 3:85 0:44]
|
接下来我们将看一下只使用 Go 协程和通道是如何实现相同的状态控制任务的。 |
下一个例子: Go 状态协程.