并发安全和锁
在狼多肉少的情况下,多个goroutine会存在资源竞争的问题,这就存在了并发安全的问题
一、资源竞争问题
多人使用同一台打印机;公共厕所人多等情况
// 多人同时使用同一台打印机
package main
import (
"fmt"
"sync"
"time"
)
var WG = sync.WaitGroup{}
func printer(str string) {
for i:=0;i<len(str) ;i++ {
fmt.Printf("%c",str[i])
time.Sleep(time.Second)
}
fmt.Printf("\n")
}
func Person1() {
fmt.Println("person1执行了")
// 使用打印机
printer("hello")
WG.Done()
}
func Person2(){
fmt.Println("person2执行了")
printer("world")
WG.Done()
}
func main() {
for i := 0; i < 1; i++ {
WG.Add(2)
go Person1()
go Person2()
}
WG.Wait()
fmt.Println("主协程结束")
}
// 多个gotoutine同时修改一个公共资源
package main
import (
"fmt"
"sync"
)
var wg1 sync.WaitGroup
var sum int // 一个的话结果是9
func Add(str string) {
for i := 0; i < 10; i++ {
fmt.Println(str)
fmt.Println(sum)
sum = sum + 1
}
wg1.Done()
}
func main() {
wg1.Add(2)
go Add("第一个协程")
go Add("第二个协程")
wg1.Wait()
}
二、锁机制
1.专业名词解释
-
临界区:多个goroutine同时操作一个资源
-
数据竞态:多个goroutine竞争资源的情况
2.互斥锁
使用sync包的Mutex类型来实现互斥锁
- lock上锁(获取token)
- Unlock解锁(释放token)
特点:
- 写操作之间互斥
- 读操作之间互斥
- 写操作和读操作之间互斥
使用场景:
- 读写次数没有明显区别的场景
- 写大于读操作的
代码示例:
package main
import (
"fmt"
"sync"
"time"
)
var WG sync.WaitGroup
var lock sync.Mutex
func printer(str string) {
lock.Lock()
for i:=0;i<len(str) ;i++ {
fmt.Printf("%c",str[i])
time.Sleep(time.Second)
}
lock.Unlock()
fmt.Printf("\n")
}
func Person1() {
fmt.Println("person1执行了")
// 使用打印机
printer("hello")
WG.Done()
}
func Person2(){
fmt.Println("person2执行了")
printer("world")
WG.Done()
}
func main() {
WG.Add(2)
go Person2()
go Person1()
WG.Wait()
fmt.Println("主协程结束")
}
3.读写锁
使用sync包中的RWMutex类型来实现读写锁
- 写操作
- Lock:加写锁
- Unlock:解写锁
- 读操作
- RLock(): 加读锁
- RUnlock():解读锁
特点:
- 写操作之间互斥
- 读操作和写操作互斥
- 读操作和读操作之间_不互斥_
使用场景:
- 适合读多写少的场景
代码示例:
package main
import (
"sync"
"fmt"
"time"
)
var sum1 int
var rwmutex sync.RWMutex
func Write() {
rwmutex.Lock()
sum1 ++
fmt.Printf("写入数据正常,加完后的值是:%d\n",sum1)
rwmutex.Unlock()
}
func Read() {
rwmutex.RLock()
fmt.Printf("读数据正常,读到的值是:%d\n",sum1)
rwmutex.RUnlock()
}
func main() {
//swg1.Add(2)
for i:=0;i<10;i++{
go Write()
}
for j:=0;j<10;j++ {
go Read()
}
time.Sleep(time.Second * 20)
//swg1.Wait()
}
三、单例(sync.Once)
1.once的使用
package main
import (
"fmt"
"sync"
)
var once sync.Once
func Hello() {
fmt.Println("hello world")
}
func main() {
for i:=0;i<10;i++{
once.Do(Hello)
}
}
2.单例实现
package main
import (
"sync"
"fmt"
)
var once sync.Once
type Person struct {
Name string
}
var person *Person
func NewPerson(name string)*Person {
once.Do(func() {
person = new(Person)
person.Name = name
})
return person
}
func main() {
//p1 := &Person{Name:"hallen1"}
//p2 := &Person{Name:"hallen2"}
//fmt.Printf("%p\n",p1)
//fmt.Printf("%p\n",p2)
p1 := NewPerson("hallen1")
p2 := NewPerson("hallen2")
fmt.Printf("%p\n",p1)
fmt.Printf("%p\n",p2)
}