go协程和锁| 青训营笔记

60 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第2天

协程

并发与并行:Go可以发挥多核优势,高效运行

image-20230116155339374

协程:用户态,轻量级线程,栈MB级别

线程:内核态,线程跑多个协程,栈KB级别

image-20230116155512314

使用go关键字开启协程

image-20230116161142505

协程间通信:

image-20230116161112424

Channel:

make(chan 元素类型,[缓冲大小])

  • 无缓冲通道 make(chan int)
  • 有缓冲通道 make(chan int,2)

image-20230116161021072

  1. 子协程发送0~9数字
  2. 子协程计算输入数字的平方
  3. 主协程输出最后的平方数

image-20230116161516777 image-20230116161535534

Go语言中使用sync包的Mutex类型来实现互斥锁

 var x int64
 var wg sync.WaitGroup
 var lock sync.Mutex
 ​
 func add() {
     for i := 0; i < 5000; i++ {
         lock.Lock() // 加锁
         x = x + 1
         lock.Unlock() // 解锁
     }
     wg.Done()
 }

读写互斥锁:

互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型。

读写锁分为两种:读锁和写锁。当一个goroutine获取读锁之后,其他的goroutine如果是获取读锁会继续获得锁,如果是获取写锁就会等待;当一个goroutine获取写锁之后,其他的goroutine无论是获取读锁还是写锁都会等待。

读读共享,读写互斥,写写互斥

 func write() {
     // lock.Lock()   // 加互斥锁
     rwlock.Lock() // 加写锁
     x = x + 1
     time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒
     rwlock.Unlock()                   // 解写锁
     // lock.Unlock()                     // 解互斥锁
     wg.Done()
 }
 ​
 func read() {
     // lock.Lock()                  // 加互斥锁
     rwlock.RLock()               // 加读锁
     time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒
     rwlock.RUnlock()             // 解读锁
     // lock.Unlock()                // 解互斥锁
     wg.Done()
 }

sync.WaitGroup:

在代码中生硬的使用time.Sleep肯定是不合适的,Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法:

方法名功能
(wg * WaitGroup) Add(delta int)计数器+delta
(wg *WaitGroup) Done()计数器-1
(wg *WaitGroup) Wait()阻塞直到计数器变为0