sync.Mutex
互斥量(mutual exclusio,简称 mutex)。sync包中的Mutex就是与其对应的类型,该类型的值可以被称为互斥量或者互斥锁。
一个互斥锁可以被用来保护一个临界区或者一组相关临界区。我们可以通过它来保证,在同一时刻只有一个 goroutine 处于该临界区之内。
为了兑现这个保证,每当有 goroutine想进入临界区时,都需要先对它进行锁定,并且,每个 goroutine 离开临界区时,都要及时地对它进行解锁。
锁定操作可以通过调用互斥锁的Lock方法实现,而解锁操作可以调用互斥锁的Unlock方法。
使用互斥锁的注意事项如下:
1.不要重复锁定互斥锁;
2.不要忘记解锁互斥锁,必要时使用defer语句;
3.不要对尚未锁定或者已解锁的互斥锁解锁;
4.不要在多个函数之间直接传递互斥锁。
让每一个互斥锁都只保护一个临界区或一组相关临界区。
一旦我们声明了一个sync.Mutex类型的变量,就可以直接使用它了。不过要注意,该类型是一个结构体类型,属于值类型中的一种。把它传给一个函数、将它从函数中返回、把它赋给其他变量、让它进入某个通道都会导致它的副本的产生。
并且,原值和它的副本,以及多个副本之间都是完全独立的,它们都是完全独立的,它们都是不同的互斥锁。
如果你把一个互斥锁作为参数值传给了一个函数,那么在这个函数中对传入的锁的所有操作,都不会对存在于该函数之外的那个原锁产生任何的影响。
package main
import (
"fmt"
"sync"
"time"
)
// SafeCounter 的并发使用是安全的。
type SafeCounter struct {
v map[string]int
mux sync.Mutex
}
// Inc 增加给定 key 的计数器的值。
func (c *SafeCounter) Inc(key string) {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
c.v[key]++
c.mux.Unlock()
}
// Value 返回给定 key 的计数器的当前值。
func (c *SafeCounter) Value(key string) int {
c.mux.Lock()
// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
defer c.mux.Unlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
for i := 0; i < 1000; i++ {
go c.Inc("somekey")
}
time.Sleep(time.Second)
fmt.Println(c.Value("somekey"))
}
sync.RWMutex
读写锁是读 / 写互斥锁的简称。在 Go 语言中,读写锁由sync.RWMutex类型的值代表。与sync.Mutex类型一样,这个类型也是开箱即用的
一个读写锁中实际上包含了两个锁,即:读锁和写锁。sync.RWMutex类型中的Lock方法和Unlock方法分别用于对写锁进行锁定和解锁,而它的RLock方法和RUnlock方法则分别用于对读锁进行锁定和解锁。
一个读写锁中实际上包含了两个锁,即:读锁和写锁。sync.RWMutex类型中的Lock方法和Unlock方法分别用于对写锁进行锁定和解锁,而它的RLock方法和RUnlock方法则分别用于对读锁进行锁定和解锁。
对于同一个读写锁来说有如下规则:
在写锁已被锁定的情况下再试图锁定写锁,会阻塞当前的 goroutine。
在写锁已被锁定的情况下试图锁定读锁,也会阻塞当前的 goroutine。
在读锁已被锁定的情况下试图锁定写锁,同样会阻塞当前的 goroutine。
在读锁已被锁定的情况下再试图锁定读锁,并不会阻塞当前的 goroutine。
对于某个受到读写锁保护的共享资源,多个写操作不能同时进行,写操作和读操作也不能同时进行,但多个读操作却可以同时进行。