概述
sync包文档:Package sync - The Go Programming Language
sync包提供了基本同步的操作,如互斥锁等。除了Once和 WaitGroup之外,大多数类型都是提供低级库例程使用的。而更高级的的同步最好是通过channel和通信来完成。
互斥锁 Mutex
互斥锁比较暴力,当一个goroutine获得了互斥锁之后,其他的goroutine只能等待释放锁。
使用互斥锁时,对资源操作完一定要解锁,否则程序会死锁。建议使用defer语句来解锁。
//定义互斥锁对象
var mutex_name sync.Mutex
//上锁
mutex_name.Lock()
//解锁
mutex_name.Unlock()
同样是的火车站售票问题,我们看一下通过互斥锁解决临界资源问题的效果
//车票余量
var tickets = 5
//定义同步等待组
var wg sync.WaitGroup
//定义互斥锁对象
var mutex sync.Mutex
func main() {
wg.Add(3)
go saleTickets("1号售票口")
go saleTickets("2号售票口")
go saleTickets("3号售票口")
wg.Wait()
}
func saleTickets(name string) {
for {
mutex.Lock()
if tickets > 0 {
fmt.Printf("车票剩余:%v, %v售出\n", tickets, name)
tickets--
time.Sleep(1 * time.Second)
} else {
mutex.Unlock()
fmt.Println("车票已售罄...")
wg.Done()
break
}
mutex.Unlock()
}
}
读写锁 RWMutex
读写锁和普通的互斥锁不同的是,它是一种专门针对读操作和写操作的互斥锁。
读写锁同一时刻允许有多个读操作在进行,但在同一时刻只允许有一个写操作在进行。并且在某一个写操作进行过程中,读操作也是不可以进行的。所以说,读写锁对于读操作可以同时并发,但是对于写操作是完全互斥的。
//定义读写锁锁对象
var rwmutex_name sync.RWMutex
//读锁
rwmutex_name.RLock
//读解锁
rwmutex_name.RUnlock
//写锁
rwmutex_name.Lock
//写解锁
rwmutex_name.Unlock
读写锁我们就不进行示例的展示了,因为和上面互斥锁展示的示例效果应该差别不大。
对写锁实际解决的问题是,使用互斥锁带来并发能力降低的问题。因为互斥锁不论读操作还是写操作都会阻塞,程序从并发变成了串行。
而实际上对于不会对临界资源产生修改的读操作,我们是不需要阻塞的;只有对临界资源需要修改的写操作,我们才需要阻塞。所以读写锁就是为了提高程序加锁时的并发能力。