Golang 基础值速学之二十一(读写锁互斥锁)

我们都知道golang当中协程是很牛逼的,也是它的强项,比如你起来20个协程同时去处理一个数据,那么这20个协程之间有可能会出现2个或者多个协程同时操作一个数据的问题,那么就容易造成数据的错误!为了保证数据的绝对正确,所以引出来了互斥锁!
我们下来看一下案例,上代码:

package main
func main(){
    for r := 0; r<20; r++{
       wg.Add(1)
       go test111()
    }
    wg.Wait()
}

var count int
var wg sync.WaitGroup
var mutex sync.Mutex
func test111(){
  //mutex.Lock()
  count++
  fmt.Println("the count is :",count)
  time.Sleep(time.Millisecond)
  //mutex.Unlock()
  wg.Done()
}

找到该文件所在位置然后在dos命令行运行:

go build -race aa.go

然后就会生成一个aa.exe文件
这个时候你再执行

aa.exe

Golang 基础值速学之二十一(读写锁互斥锁)

Golang 基础值速学之二十一(读写锁互斥锁)

这样就可以看到内部的竞争关系 发现9 1 2 6 7 3 4 都存在竞争关系!
这样是不是很清楚啦!
尤其是在高流量大并发的情况下 如果不利用互斥锁那么很容易就会造成多个协程同时对一个数据争抢的现象发生!
所以呢我们需要研究研究互斥锁

互斥锁是传统并发编程当中对共享资源进行访问控制的主要手段,它由标准库sync中的Mutex结构体类型表示。sync.Mutex类型只有两个公开的指针方法,Lock和Unlock。Lock锁定当前的共享资源,Unlock进行解锁

看代码:

package main
func main(){
    for r := 0; r<20; r++{
       wg.Add(1)
       go test111()
    }
    wg.Wait()
}

var count int
var wg sync.WaitGroup
var mutex sync.Mutex
func test111(){
    //因为起来了20个协程 这20个协程之间一定会存在竞争关系 都会去改变count的值 所以容易造成错误的发生 加上了互斥锁之后 一个 协程锁定了那么其他协程无法操作 只有等到当前协程解锁之后才能继续操作!
    //加锁
    mutex.Lock()
  count++
  fmt.Println("the count is :",count)
  time.Sleep(time.Millisecond)
  //解锁
  mutex.Unlock()
  wg.Done()
}

然后再执行

go build -race aa.go
aa.exe

你会发现就不存在竞争的关系了!
一旦锁定那么其他协程只能等待当前协程结束才能继续对count进行操作!
互斥锁一般会用到写上比如操作数据库的时候只能每次写入一条那么就可以用互斥锁,在秒杀场景当中也是会用到互斥锁!
互斥锁的本质是当一个协程访问的时候,其他协程都不能访问,这样在资源同步避免竞争的同时也降低了程序的并发性能,程序由原来的并行执行变成了串行执行!

其实,当我们对一个不会变化的数据只做读操作的话是不存在资源竞争的问题的,因为数据是不变的,不管怎么读取多少协程同时读取都是可以的,所以问题不是出在读上,主要是修改,也就是写,修改的数据要同步,这样其他协程才可以感知到,所以真正互斥应该是读取和修改,修改和修改之间,读和读是没有互斥操作的必要的!

因此衍生出另外一种锁,叫做读写锁!

读写锁可以让多个读操作并发,同时读取!但是对于写操作是完全互斥的。也就是说,当一个协程进行写操作的时候,其他协程既不能进行读操作也不能进行写操作!

go当中的读写锁由结构体类型sync.REMutex表示

关于读写锁我们直接上代码吧:

package main
func main(){
//读写锁案例
    //开启10个协程执行读操作
    for i:=0;i<10;i++{
        wg.Add(1)
        go write()
    }
    //开启10个协程执行写操作
    for i:=0;i<10;i++{
        wg.Add(1)
        go read()
    }
    wg.Wait()
}
//这里是 sync.RWMutex  读写锁
var mutex1 sync.RWMutex
//以下是为了配合读写锁的相关代码
//写的方法
func write(){
   mutex1.Lock()
   fmt.Println("执行写操作")
   time.Sleep(time.Second * 2)
   mutex1.Unlock()
   wg.Done()
}
//读的方法
func read(){
   mutex1.RLock()
   fmt.Println("执行读操作")
   time.Sleep(time.Second * 2)
   mutex1.RUnlock()
   wg.Done()
}

互斥锁读写锁就这么简单!在并发的时候记得合理使用!

本作品采用《CC 协议》,转载必须注明作者和本文链接
胡军
讨论数量: 1

sync.REMutex还是sync.RWMutex

3年前 评论
huxiaobai_001 (楼主) 3年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!