go快速学习_常见锁以及使用

锁是异步中进行协调使用的常用工具,无论是gcd还是其他,都有用过。

而每一种语言的锁都是通用的,比如说互斥,读写等

死锁

  • 不是锁的一种,是错误使用锁的现象
  • 常见死锁:
  • 单go程使用同一个channel自读自写
  • 多个go程使用channel通信,go程创建之前,对channel进行读写
  • 多个go程使用多个channel通信,相互依赖造成死锁
  • 多个go程使用锁(读写锁、互斥锁)和channel通信

互斥锁 mutex

  • 作用:保护公共区,防止多个控制流共同访问共享资源时造成数据混乱
  • 建议锁,不具备强制性
  • 使用注意事项:
    • 访问公共区之前,加锁。访问结束立即结束
    • 锁的粒度越小越好
  • 使用方法:
    • 创建互斥量var mutex sync.Mutex
    • 加锁 mutex.Lock()
      • …….访问公共区
    • 解锁mutex.Unlock()

读写锁

  • 特性:
    • 读时共享,写时独占
    • 写锁优先级高
    • 读写锁只有一把,但具有两种属性(r/w) 加锁时根据使用的需求选择加锁属性
  • 使用方法:
    • 创建互斥量var mutex sync.RWMutex
    • 加读锁 mutex.RLock() 加写锁 mutex.Lock()
      • …….访问公共区
    • 解读锁mutex.RUnlock() 解写锁mutex.RUnlock()
  • 建议:尽量不要将锁和channel混合使用——条件变量

条件变量

  • ==条件变量不是锁的一种==!要结合锁使用

条件变量所使用的函数

  • wait方法

    • func (c *Cond)wait()
      
      • 阻塞等待条件变量满足
      • 释放已经掌握的互斥锁,要求在调用wait之前,先加锁 ……阻塞等待条件变量满足
      • 被唤醒时,解除阻塞,重新加锁
  • signal方法

    • func (c*Cond)Signal()
      
      • 唤醒阻塞在条件变量上的go程
  • broadcast方法

    • func (c*Cond)Broadcast()
      
      • 唤醒阻塞在条件变量上的所有go程

条件变量实现步骤:

  1. 创建条件变量 var cond sync.Cond

  2. 初始化条件变量的成员L,指定锁(互斥,读写) cond.L = new(sync.Mutex)

  3. 加锁 cond.L.Lock()

  4. 判断条件变量满足,调用wait函数

    for len(ch) == cap(ch){ //此处必须使用for 循环判断 wait是否满足条件
      cond.wait() 
    }
    
  5. 产生数据,写入公共区

  6. 唤醒阻塞在条件变量上的对端 cond.Signal()

  7. 解锁 cond.L.UnLock()

示例

//创建条件变量
var cond sync.Cond
// 进行单向读管道
func userR(r <-chan int) {
   for { // 死循环读取
      cond.L.Lock()  //必须要先加锁
      for len(r) == 0 { // 官方推荐使用for判断,用if会执行一段时间后阻塞 
         cond.Wait()
      }
      fmt.Println("=====读:",<-r)
      cond.Signal() // 唤醒写端
      cond.L.Unlock()  //解锁
   }
}
//同读端 
func userW(w chan<- int) {
   for {
      cond.L.Lock()
      for cap(w) == len(w)  {
         cond.Wait()
      }
      rang := rand.Intn(3000)
      w <- rang
      fmt.Println("--写",rang)
      cond.Signal()
      cond.L.Unlock()
   }
}

func Ch() {
   rand.Seed(time.Now().UnixNano())
   ch := make(chan int, 5)
   cond.L = new(sync.Mutex)
   for i := 0; i <= 1000; i++ {
      go userR(ch)
      go userW(ch)
   }

   for {
      //防止主函数结束,子函数跟着销毁
      runtime.GC()
   }

}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容