本教材由知了传课辛苦制作而成,仅供学习使用,请勿用于商业用途!如进行转载请务必注明出处!谢谢!

并发安全和锁

在狼多肉少的情况下,多个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)


}

1301人已阅读,今天你学习了吗?

添加新回复