互斥锁和读写锁
<p>在并发编程中存在多个goroutine协程共用同一条数据,也就是临界资源的安全问题,为了解决这种的问题Go语言提供了同步等待组的方法解决,Go语言还提供了锁,控制并发情况下的数据安全。Go语言提供两种锁类型<code>互斥锁</code>和<code>读写锁</code>。当一个协程在访问当前数据资源的时候,给当前资源加上锁, 防止另外的协程访问,等待解锁后其他协程才能够访问。</p>
<h2>互斥锁</h2>
<p>互斥锁,当一个goroutine获得锁之后其他的就只能等待当前goroutine执行完成之后解锁后才能访问资源。对应的方法有<code>上锁Lock()</code>和<code>解锁Unlock()</code>。
<img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/6/23/172e05feaf31a6e1~tplv-t2oaga2asx-zoom-in-crop-mark:3024:0:0:0.awebp" alt="" /></p>
<pre><code>package main
import (
&quot;fmt&quot;
&quot;sync&quot;
)
//定义全局变量 表示救济粮食总量
var food = 10
//同步等到组对象
var wg sync.WaitGroup
//创建一把锁
var mutex sync.Mutex
func main() {
wg.Add(4)
//开启4个协程抢粮食
go Relief(&quot;灾民好家伙&quot;)
go Relief(&quot;灾民好家伙2&quot;)
go Relief(&quot;灾民老李头&quot;)
go Relief(&quot;灾民老李头2&quot;)
wg.Wait() //阻塞主协程,等待子协程执行结束
}
//定义一个发放的方法
func Relief(name string) {
defer wg.Done()
for {
//上锁
mutex.Lock()
if food &gt; 0 { //加锁控制之后每次只允许一个协程进来,就会避免争抢
food--
fmt.Println(name, &quot;抢到救济粮 ,还剩下&quot;, food, &quot;个&quot;)
} else {
mutex.Unlock() //条件不满足也需要解锁 否则就会造成死锁其他不能执行
fmt.Println(name, &quot;别抢了 没有粮食了。&quot;)
break
}
//执行结束解锁,让其他协程也能够进来执行
mutex.Unlock()
}
}</code></pre>
<p><strong>在使用互斥锁的时候一定要进行解锁,否则会造成程序的死锁</strong>。</p>
<h2>读写锁</h2>
<p>互斥锁是用来控制多个协程在访问同一个资源的时候进行加锁控制,保证了数据安全,但同时也降低了性能,如果说多个goroutine同时访问一个数据,只是读取一下数据,并没有对数据进行任何修改操作,那么不管多少个goroutine来读取都应该是可以的。主要问题在于修改。修改的数据就需要加锁操作,来保证数据在多个goroutine读取的时候统一。 读取和读取之间是不需要互斥操作的,所以我们用<code>读写锁</code>专门针对读操作和写操作的互斥锁。</p>
<p><img src="https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2020/6/23/172dfad2aa88af6e~tplv-t2oaga2asx-zoom-in-crop-mark:3024:0:0:0.awebp" alt="" /></p>
<pre><code>package main
import (
&quot;fmt&quot;
&quot;sync&quot;
&quot;time&quot;
// &quot;runtime&quot;
)
//创建一把读写锁 可以是他的指针类型
var rwmutex sync.RWMutex
var wg sync.WaitGroup
func main() {
wg.Add(3)
go ReadTest(1)
go WriteTest(2)
go ReadTest(3)
wg.Wait()
fmt.Println(&quot;======main结束======&quot;)
}
//读取数据的方法
func ReadTest(i int) {
defer wg.Done()
fmt.Println(&quot;======准备读取数据======&quot;)
rwmutex.RLock() //读上锁
fmt.Println(&quot;======正在读取...&quot;, i)
rwmutex.RUnlock() //读取操作解锁
fmt.Println(&quot;======读取结束======&quot;)
}
func WriteTest(i int) {
defer wg.Done()
fmt.Println(&quot;======开始读写数据======&quot;)
rwmutex.Lock() //写操作上锁
fmt.Println(&quot;======正写数据...&quot;, i)
time.Sleep(1 * time.Second)
rwmutex.Unlock() //写操作解锁
fmt.Println(&quot;======写操作结束======&quot;)
}</code></pre>
<p>与普通的互斥锁的区别在于他能分别针对读操作和写操作进行锁定和解锁。读写锁的控制规则是,他允许任意多个读操作同时进行,但是只允许一个写操作进行,并且在某一个写操作的时候不允许读操作进行。</p>