一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第21天,点击查看活动详情。
资源竞争
当同一块内存被多个goroutine同时访问,就会产生不知道谁先访问以及无法预料最后结果得情况,此时这块内存称为共享资源,这种情况称为资源竞争。
var sum = 0
func main() {
//开启1000个协程让sum+10
for i := 0; i < 1000; i++ {
go add(10)
}
time.Sleep(2 * time.Second)
fmt.Println("sum:", sum)
}
func add(i int) {
sum += i
}
上面得代码通过开启1000个协程让sum加10演示资源竞争的问题,正常情况下应该输出10000,但是事实上输出结果却不可预期。这是因为1000个协程同时访问操作一个变量,有可能某个协程与其他协程一起操作造成结果变小。为了保证并发安全,我们需要保证只有一个协程去执行这个操作。
sync.Mutex
互斥锁指的是在同一时刻只有一个协程执行某段代码,其他协程都要等待该协程执行完毕后才能继续执行。
var (
sum int
mutex sync.Mutex
)
func add(i int) {
mutex.Lock()
sum += i
mutex.Unlock()
}
以上被加锁保护的代码又称临界区(访问无法同时被多个协程访问的共享资源的程序片段)。互斥锁有Lock和Unlock两个操作,一个协程获得锁后其他协程只有等锁释放后才能获取这个Lock和Unlock方法。
互斥锁通常成对出现,一般采用defer去保证锁最后被释放。
func add(i int) {
mutex.Lock()
defer mutex.Unlock()
sum += i
}
另外如果上面的程序同时需要读取sum的值,也可以采用互斥锁,这样读取的操作会在操作共享资源之后执行,保证读取的是有效的值。