并发中的问题
并发编程中,有时候会有需要共享资源的场景,这时候并发就有可能出现一些问题。
package main
import (
"fmt"
"sync"
)
var (
x = 0
wg sync.WaitGroup
)
// 自增一万次
func add() {
for i := 0; i < 10000; i++ {
x++
}
wg.Done()
}
func main() {
wg.Add(10)
for i := 0; i < 10; i++ {
go add()
}
wg.Wait()
fmt.Println(x)
}
如上面这段代码,x是一个共享变量,当10个goroutine同时去让x自增一万次时,期望得到的结果应该是100000,但实际结果不可预测:
PS D:\Go\lock> go run main.go
41979
PS D:\Go\lock> go run main.go
52654
PS D:\Go\lock> go run main.go
47636
结果每次都不相同,这是因为x++并不是一个原子操作,它分为x+1与将x+1赋值给x两部分,当某一个goroutine将x赋值为50000时,可能下一个goroutine又会把30000赋值给x,这就导致了最后x的结果不是正确的结果。
锁
解决上面遇到的问题的方法就是加锁,使x的修改具有唯一性,即一次只能有一个goroutine修改x;
sync.Mutex
package main
import (
"fmt"
"sync"
)
var (
x = 0
// 声明一个锁
lock sync.Mutex
wg sync.WaitGroup
)
func add() {
for i := 0; i < 10000; i++ {
// 加锁
lock.Lock()
x++
// 解锁
lock.Unlock()
}
wg.Done()
}
func main() {
wg.Add(10)
for i := 0; i < 10; i++ {
go add()
}
wg.Wait()
fmt.Println(x)
}
再x++执行前加锁,在其执行后解锁,这样无论执行多少次,结果都是正确的结果
PS D:\Go\lock> go run main.go
100000
PS D:\Go\lock> go run main.go
100000
PS D:\Go\lock> go run main.go
100000
sync.RWMutex
sync.Mutex是一个普通锁,即无论是读操作还是写操作都会加锁,而读操作不会影响资源,无论读多少次,资源都不会变,所以读的时候,其它goroutine完全没有必要等待解锁后才能执行读操作。
sync.RWMutex是一个读写锁,在一个goroutine获得读锁的时候,其它goroutine无需等待,可以继续获得读锁,只有当一个goroutine获得写锁的时候,其它goroutine才需要等待锁释放后才能继续获得锁,因为写操作影响了资源,写之前和写之后资源是不一样的,所以需要等待写锁释放后,资源已经被改变,才能继续获得锁以保证资源是正确的。
sync.RWMutex的使用和sync.Mutex差不多
package main
import (
"fmt"
"sync"
"time"
)
var (
x = 0
// 声明一个锁
lock sync.Mutex
wrLock sync.RWMutex
wg sync.WaitGroup
)
func read() {
for i := 0; i < 10000; i++ {
// 加读锁
wrLock.RLock()
// 加普通锁
//lock.Lock()
//fmt.Println()
// 解读锁
wrLock.RUnlock()
// 解普通锁
//lock.Unlock()
}
wg.Done()
}
func write() {
for i := 0; i < 100; i++ {
// 加写锁
wrLock.Lock()
// 加普通锁
//lock.Lock()
x++
// 解写锁
wrLock.Unlock()
// 解普通锁
//lock.Unlock()
}
wg.Done()
}
func main() {
oldTime := time.Now()
wg.Add(10)
for i := 0; i < 10; i++ {
go write()
}
wg.Add(10000)
for i := 0; i < 10000; i++ {
go read()
}
wg.Wait()
fmt.Println("消耗时间:", time.Since(oldTime))
}
对比下时间消耗
读写锁:
消耗时间: 4.0886281s
普通锁:
消耗时间: 12.0778299s
通过对比可以看出,本例中两种锁的时间消耗差别是很大的。
end
文章仅做个人学习交流,也许并不完全正确,欢迎指正;