保证同步访问共享资源的安全访问,消除竞争状态的方式有三种: 原子函数、互斥锁、通道
无缓冲通道:
当一个goroutine向通道发送,此时没有goroutine接收时,会导致发送goroutine阻塞,只有通道被接收后。两个goroutine才被释放出来,可以去处理各自的工作。任意一个操作都无法离开另一个操作单独存在。
互斥锁
互斥锁用于在代码上创建一个临界区,保证同一时间只有一个goroutine可以执行这个临界区代码。
package main
import (
"fmt"
"runtime"
"sync"
)
var (
counter int
wg sync.WaitGroup
mutex sync.Mutex
)
func main() {
wg.Add(2) //表示要等待2个goroutine
go incCounter(1)
go incCounter(2)
wg.Wait() //等待goroutine结束
fmt.Printf("Final Counter:%d\n", counter)
}
func incCounter(id int) {
defer wg.Done() //在函数退出时,调用Done来通知main函数工作已经完成,goroutine结束
for count := 0; count < 2; count++ {
mutex.Lock() //加锁,创建临界区
value := counter
runtime.Gosched()
value++
counter = value
mutex.Unlock() //解锁
}
}
output:
Finale Counter:4
无缓冲通道
在网球比赛中,两位选手会把球在两个人之间来回传递。选手总是处在以下两种状态之一:要么在等待接球,要么将球打向对方。使用两个goroutine来模拟网球比赛中的两个人,使用无缓冲通道来模拟球的来回。
当一方接球失败,关闭通道。另一方收到通道关闭,则说明自己won
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
var wg sync.WaitGroup
//init函数在main函数之前运行
func init() {
/* 初始化随机数种子,默认随机数种子为Seed(1),对程序而言属于产生伪随机数。
即当程序每次重新开始运行,结果都一致。
选定不同的随机种子,每次运行产生不一样的随机数。
*/
rand.Seed(time.Now().UnixNano())
}
func main() {
court := make(chan int) //无缓存通道,模拟网球
wg.Add(2) //等待两个goroutine
go player("Vera", court) //选手Vera
go player("hannah", court) //选手Hannah
court <- 1 //发球
wg.Wait() //等待游戏结束
}
func player(name string, court chan int) {
defer wg.Done() //函数退出时通知main函数goroutine已结束
for {
ball, ok := <-court //等待球被打过来,接球
if !ok { //接收到通道关闭,则赢了
fmt.Printf("player %s won\n", name)
return
}
//选择随机数,利用这个数来判断是否球是否接到
n := rand.Intn(100)
if n%13 == 0 { //没接到球
fmt.Printf("player %s Missed\n", name)
close(court) //关闭通道,游戏结束
return
}
//接到球,继续打
fmt.Printf("player %s Hit %d\n", name, ball)
ball++
court <- ball //将球打向对方
}
}
output:
player hannah Hit 1
player Vera Hit 2
player hannah Hit 3
player Vera Hit 4
player hannah Missed
player Vera won