社区网站建设公司,wordpress建站程序,万网博通,医药外贸是做什么的临界资源安全的问题#xff1a; 临界资源#xff1a; 指并发环境中多个 进程/线程/协程 可以共享#xff08;都可以调用#xff09;的资源/变量#xff0c;如果在并发环境中处理不当#xff0c;就会造成一些 严重、问题 func main() {//临界资源a : 10go func() {a 100f…临界资源安全的问题 临界资源 指并发环境中多个 进程/线程/协程 可以共享都可以调用的资源/变量如果在并发环境中处理不当就会造成一些 严重、问题 func main() {//临界资源a : 10go func() {a 100fmt.Println(goroutine a:, a)}()a 99fmt.Println(main goroutine a:, a)time.Sleep(1)}#输出
main goroutine a: 99
goroutine a: 100临界资源安全问题 并发本身并不复杂但是有了 临界资源的竞争问题就使得我们开发出来的并发程序变的复杂起来。应为会引起很多莫名其妙的问题。如果多个 goruotine 在访问同一个数据资源的时候其中一个线程修改了数据那么这个数据就被修改了对于其他的 goroutine 来讲这个数值很可能是不对的 例如 我们通过抢购牛奶一共 10 箱6个用户一直再抢 // 临界资源 10箱牛奶
var milk 10func main() {go saleMilks(用户A)go saleMilks(用户B)go saleMilks(用户C)go saleMilks(用户D)go saleMilks(用户E)go saleMilks(用户F)time.Sleep(6 * time.Second)
}func saleMilks(name string) {rand.Seed(time.Now().UnixNano())for {if milk 0 {//模拟逻辑处理先判断临界资源 然后逻辑处理消耗时间 释放CPU资源别的 goroutine 可以继续执行time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)fmt.Println(name, 售出, milk)milk--} else {fmt.Println(售罄 没有奶了)break}}
}// 程序输出
用户D 售出 10
用户B 售出 10
用户F 售出 10
用户E 售出 10
用户C 售出 10
用户A 售出 10
用户F 售出 4
用户F 售出 3
用户B 售出 2
用户E 售出 1
售罄 没有票了
用户A 售出 0
售罄 没有票了
用户B 售出 -1
售罄 没有票了
用户F 售出 -2
售罄 没有票了
用户C 售出 -3
售罄 没有票了
用户D 售出 -4
售罄 没有票了Process finished with the exit code 0 很显然出现了 负数超卖的情况 判断临界资源是否 大于 0做相应处理。有可以是 写入 读取数据库等其他操作。没有等待响应返回的时候下一个 goroutine 已经开始执行了这个时候 就有可能 发生 临界值不一直的情况 临界资源安全问题的解决 想要解决临界资源安全问题很多编程语言的方案都是同步通过上锁的方式 某一时刻只能容许一个 goroutine 来访问这个共享数据当前 goroutine 访问完毕解锁后 其他 goroutine 才能来访问。 我们可以借助于 sync 包下的 sync.Mutex 锁操作 package mainimport (fmtmath/randsynctime
)// 临界资源 10箱牛奶
var milk 10var wg sync.WaitGroup
var mutex sync.Mutex //创建锁头func main() {wg.Add(6) //这里设置个数的意义不大应为有锁的存在 saleMilks 只容许 一个 goroutine 访问go saleMilks(用户A)go saleMilks(用户B)go saleMilks(用户C)go saleMilks(用户D)go saleMilks(用户E)go saleMilks(用户F)//time.Sleep(6 * time.Second)wg.Wait()fmt.Println(All tasks completed)
}func saleMilks(name string) {defer wg.Done()// 在goroutine中使用Mutex保护共享资源mutex.Lock()defer mutex.Unlock()rand.Seed(time.Now().UnixNano())for {if milk 0 {//模拟逻辑处理先判断临界资源 然后逻辑处理消耗时间 释放CPU资源别的 goroutine 可以继续执行time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)fmt.Println(name, 售出, milk)milk--} else {fmt.Println(name, 售罄 没有奶了)break}}
}/// 输出 这里看到 一直是 用户A 拿到资源应为第一个 goroutine 就是用户A只带他 执行完毕 自他的 goroutine 才继续执行 从侧面也印证了 资源是一直 锁的状态 没有释放锁之前 其他goroutine 进不来
用户A 售出 10
用户A 售出 9
用户A 售出 8
用户A 售出 7
用户A 售出 6
用户A 售出 5
用户A 售出 4
用户A 售出 3
用户A 售出 2
用户A 售出 1
用户A 售罄 没有奶了
用户F 售罄 没有奶了
用户C 售罄 没有奶了
用户D 售罄 没有奶了
用户E 售罄 没有奶了
用户B 售罄 没有奶了
All tasks completedgo 中临界资源的核心思路 上面 锁 的思路在 go 语言中是不提倡的,只是使用上 相对更便捷一些在 go 的并发编程中有一个很经典的话 不要以共享内存的方式去通信 而要以通信的方式共享内存。 在 go 语言中并不鼓励 用锁的方式共享资源而是鼓励通过 channel 的方式共享状态或者共享状态 变化在各个 goroutine 之间传递以通信的方式去共享内存这样同样能像锁 一样保证在同一时刻只有一个 goroutine 访问共享资源。 当然 在主流的编程语言中为了保证多线程之间的共享数据安全性和一致性都会提供一套基本的同步工具集如锁 条件变量 原子操作等等。 go语言标准库 也豪不意外提供了这些机制使用方式也和其他语言差不多。 使用通道实现锁 // 临界资源 10箱牛奶
var milk 10var ch make(chan struct{}, 1) // 创建一个有缓冲的通道作为信号量容量为1表示只能有一个goroutine可以通过func main() {go saleMilks(用户A)go saleMilks(用户B)go saleMilks(用户C)go saleMilks(用户D)go saleMilks(用户E)go saleMilks(用户F)time.Sleep(6 * time.Second)fmt.Println(All tasks completed)
}func saleMilks(name string) {ch - struct{}{} // 发送信号获取锁// 释放锁rand.Seed(time.Now().UnixNano())for {if milk 0 {//模拟逻辑处理先判断临界资源 然后逻辑处理消耗时间 释放CPU资源别的 goroutine 可以继续执行time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)fmt.Println(name, 售出, milk)milk--} else {fmt.Println(name, 售罄 没有奶了)break}}-ch // 释放锁从通道接收数据
}///输出
用户A 售出 10
用户A 售出 9
用户A 售出 8
用户A 售出 7
用户A 售出 6
用户A 售出 5
用户A 售出 4
用户A 售出 3
用户A 售出 2
用户A 售出 1
用户A 售罄 没有奶了
用户B 售罄 没有奶了
用户D 售罄 没有奶了
用户E 售罄 没有奶了
用户F 售罄 没有奶了
用户C 售罄 没有奶了
All tasks completed