互斥锁是传统的并发程序对共享资源进行访问控制的主要手段,在Go中,似乎更推崇由channel来实现资源共享和通信。它由标准库代码包sync中的Mutex结构体类型代表。只有两个公开方法:调用Lock()获得锁,调用unlock()释放锁。
-
使用Lock()加锁后,不能再继续对其加锁(同一个goroutine中,即:同步调用),否则会panic。只有在unlock()之后才能再次Lock()。异步调用Lock(),是正当的锁竞争,当然不会有panic了。适用于读写不确定场景,即读写次数没有明显的区别,并且只允许只有一个读或者写的场景,所以该锁也叫做全局锁。
-
func (m *Mutex) Unlock()用于解锁m,如果在使用Unlock()前未加锁,就会引起一个运行错误。已经锁定的Mutex并不与特定的goroutine相关联,这样可以利用一个goroutine对其加锁,再利用其他goroutine对其解锁。
建议:同一个互斥锁的成对锁定和解锁操作放在同一层次的代码块中。 使用锁的经典模式:
lck.Lock()会阻塞直到获取锁,然后利用defer语句在函数返回时自动释放锁。
举个栗子, 验证下互斥锁堵塞情况:
输出:
G0抢占中...
G0已抢占.
G1抢占中...
G2抢占中...
G3抢占中...
G0准备释放.
G0已释放.
G1已抢占.
G1已释放.
G2已抢占.
G2已释放.
G3已抢占.
G3已释放.
可以清楚的看到, 只有等互斥锁被释放了, 才能被抢占.