golang-锁的介绍和使用

互斥锁

应用场景

当多个groutine操作同一个线程不安全的公共资源时,为了安全我们使用互斥锁使得在某一时刻只有一个groutine操作该公共资源,即当有groutine在操作该共享变量(把共享变量加载到工作区,然后修改后再写入内存)时,其他线程在操作该共享变量时就会被block。

应用案例

我们对一个变量value=0进行1001000次+1的操作,我们知道正确答案应该是value=1001000=100000

当不使用互斥锁时

var wg *sync.WaitGroup=new(sync.WaitGroup)
var value int=0
wg.Add(100)

for i:=0;i<100;i++{
   go func(){
      for i:=0;i<1000;i++{
         value++
      }
      defer wg.Done()
      }()
}
wg.Wait()
fmt.Println(value)

打印出的结果为一个小于正确答案的随机值

当使用互斥锁将操作共享变量的操作上锁

var mutex sync.Mutex
var wg *sync.WaitGroup=new(sync.WaitGroup)
var value int=0
wg.Add(100)

for i:=0;i<100;i++{
   go func(){
      for i:=0;i<1000;i++{
         mutex.Lock()
         value++
         mutex.Unlock()
      }
      defer wg.Done()
      }()
}
wg.Wait()
fmt.Println(value)

打印出的结果为我们预期的100000

注意

lock与unlock操作一定要对应起来,如果在一个线程里面对同一个mutex多次进行lock而没有对应的unlock,那么就会死锁。如果在线程中对unlock过的mutex再unlock且缺乏对应的lock,则会引发一个不可恢复的恐慌。

读写锁

读写锁特点

读写锁中有两种锁即读锁和写锁,读锁之间两两互不干扰,写锁之间两两互斥,读锁和写锁互斥。写解锁会试图唤醒一个因欲进行读而被阻塞的线程,读解锁会试图唤醒一个因欲进行写而被阻塞的线程。若对一个未被 写/读 锁定的读写锁进行 写/读 解锁会引发一个不可恢复的恐慌。

应用情景

当一个公共资源同时被多个线程进行读和写的操作时,使用读写锁会更方便且开销更低。

应用案例

我们定义一个list类型为[]int,我们每隔0.5秒往list中添加一个数字1,并且读取当前list后一秒后再判断是原来读取的和一秒后的是否还是同一个list(我们用长度来判断)
不加读写锁

type List struct {
   source []int
   rwmutex sync.RWMutex
}

func(l *List)Read()[]int{
   return l.source
}

func(l *List)Write(){
   l.source=append(l.source,1)
}

func main() {

   list:=List{}
   list.source=[]int{}

   for i:=0;i<3;i++{
      go func(){
         ticker:=time.NewTicker(time.Millisecond*500)
         for{
    
            list.Write()
            <-ticker.C
           
         }
      }()
   }

   go func(){
      ticker:=time.NewTicker(time.Second*1)
      for{
        )
         oldlist:=list.Read()
         <-ticker.C
         newList:=list.Read()
         fmt.Println(len(oldlist)==len(newList))
         
      }
   }()

   time.Sleep(time.Second*7)
}

打印结果全为false,也就是读取的结果不能保证是最新的结果

加上读写锁

type List struct {
   source []int
   rwmutex sync.RWMutex
}

func(l *List)Read()[]int{
   return l.source
}

func(l *List)Write(){
   l.source=append(l.source,1)
}

func main() {

   list:=List{}
   list.source=[]int{}

   for i:=0;i<3;i++{
      go func(){
         ticker:=time.NewTicker(time.Millisecond*500)
         for{
            list.rwmutex.Lock()
            list.Write()
            <-ticker.C
            list.rwmutex.Unlock()
         }
      }()
   }

   go func(){
      ticker:=time.NewTicker(time.Second*1)
      for{
         list.rwmutex.RLock()
         oldlist:=list.Read()
         <-ticker.C
         newList:=list.Read()
         fmt.Println(len(oldlist)==len(newList))
         list.rwmutex.RUnlock()
      }
   }()

   time.Sleep(time.Second*7)
}

打印的全为true,因为加上读写锁后,在读未完成新旧list对比之前往list添加数字的操作是阻塞的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
golang-lru 是一个 Golang 语言实现的 LRU 缓存库,它提供了一个简单易用的 API 用于创建和使用 LRU 缓存。 下面是一个简单的使用示例: ```go package main import ( "fmt" "github.com/hashicorp/golang-lru" ) func main() { // 创建一个 LRU 缓存,容量为 2 cache, _ := lru.New(2) // 添加两个元素到缓存中 cache.Add("key1", "value1") cache.Add("key2", "value2") // 从缓存中获取一个元素 if v, ok := cache.Get("key1"); ok { fmt.Println(v.(string)) } // 添加一个新元素到缓存中,此时缓存容量已满,会自动淘汰最久未使用的元素 "key2" cache.Add("key3", "value3") // 遍历缓存中的所有元素 for _, k := range cache.Keys() { if v, ok := cache.Get(k); ok { fmt.Println(k, v.(string)) } } } ``` 运行上述代码,将会输出: ``` value1 key1 value1 key3 value3 ``` 在这个示例中,我们首先使用 `lru.New()` 函数创建了一个容量为 2 的 LRU 缓存。然后我们添加了两个元素到缓存中,并从缓存中获取了一个元素。接着我们添加了一个新元素,此时缓存已满,会自动淘汰最久未使用的元素 "key2"。最后我们遍历了缓存中的所有元素,输出了它们的键和值。 除了 `Add()` 和 `Get()` 方法外,golang-lru 还提供了 `Remove()` 和 `Contains()` 方法来删除和判断缓存中是否存在某个元素,以及 `Len()` 和 `Clear()` 方法来获取缓存中元素的数量和清空缓存。 golang-lru 还支持并发安全,你可以通过 `NewWithOptions()` 函数创建一个并发安全的 LRU 缓存,具体用法请参考官方文档:https://pkg.go.dev/github.com/hashicorp/golang-lru。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值