阅读须知:
本篇文章面向的读者: 已经基本掌握Go中的 协程(goroutine),通道(channel),互斥锁(sync.Mutex),读写锁(sync.RWMutex) 这些知识。如果对这些还不太懂,可以先回去把这几个知识点解决了。
首先理解以下三点再进入正题:
- Go中的一个协程 可以理解成一个独立的人,多个协程是多个独立的人
- 多个协程都需要访问的 共享资源(比如共享变量) 可以理解成 多人要用的某种公共社会资源
- 上锁 其实就是加入到某个共享资源的争抢组中;上锁完成 就是从争抢组中被选出,得到了期待的共享资源;解锁 就是退出某个共享资源的争抢组。
正题:
假如有这样一个现实场景:在一个公园中有一个公共厕所,这个厕所一次只能容纳一个人上厕所,同时这个厕所中有个放卷纸的位置,其一次只能放一卷纸,一卷纸的总长度是 5 米,而每个人上一次厕所需要用掉 1 米的纸。而当一卷纸用完后,公园管理员要负责给厕所加上一卷新纸,以便大家可以继续使用厕所。 那么对于这个单人公共厕所,大家只能排队上厕所,当每个人进到厕所的时候,当然会把厕所门锁好,以便任何人都进不来(包括管理员)。管理员若要进到厕所查看用纸情况并加卷纸,也需要排队(因为插队总是不文明对吧)。
那么怎么用 Golang 去模拟上述场景呢?
首先我们先不用 sync.Cond,看如何实现?那么请看下面这段代码:
package main
import (
"fmt"
"time"
"sync"
)
var 卷纸 int
var m sync.Mutex
var wg sync.WaitGroup
func 上厕所(姓名 string){
m.Lock()
defer func(){
m.Unlock()
wg.Done()
}()
fmt.Printf("%s 进到厕所\t",姓名)
if 卷纸 >= 1 { // 进到厕所第一件事是看还有没有纸
fmt.Printf("正在拉屎中...\n")
time.Sleep(time.Second)
卷纸 -= 1
fmt.Printf("%s 已用完厕所,正在离开\n",姓名)
return
}
fmt.Printf("发现纸用完了,无奈先离开厕所\n")
}
func 加厕纸(){
m.Lock()
defer func(){
m.Unlock()
wg.Done()
}()
fmt.Printf("公园管理员 进到厕所\t")
if 卷纸 <= 0 { // 管理员进到厕所是看纸有没有用完
fmt.Printf("公园管理员 正在加新纸...\n")
time.Sleep(time.Millisecond*500)
卷纸 = 5
fmt.Printf("公园管理员 已加上新厕纸,正在离开\n")
}else{
fmt.Printf("发现纸还没用完,先离开厕所\n")
}
}
func main() {
卷纸 = 5 // 厕所一开始就准备好了一卷纸,长度5米
要排队上厕所的人 := [...]string{"老王","小李","老张","小刘","阿明","欣欣","西西","芳芳"}
for _,谁 :&