互斥锁与读写锁使用

来源:互联网 发布:数据交换共享 编辑:程序博客网 时间:2024/06/03 20:31

概述

sync.Mutex和sync.RWMutex是Go语言底层基础对象,用于构建多个goroutine间的同步逻辑,当多个协程需要对共享数据读写时用到。具体实现极为简洁,性能也有保证。

使用场景

举例:1.多个协程操作同一个文件 2.生产者消费者模型     具体实例我就以最简单的打印方式说明

代码

互斥锁func print(t *testing.T, i int, wg *sync.WaitGroup, mutex *sync.Mutex) {    // mutex.Lock()    t.Logf("routine i=%d start!", i)    time.Sleep(time.Millisecond * 10)    t.Logf("routine i=%d end!", i)    wg.Done()    // mutex.Unlock()}func TestSync(t *testing.T) {    runtime.GOMAXPROCS(runtime.NumCPU())    var wg = new(sync.WaitGroup)    var mutex = &sync.Mutex{}    for i := 0; i < 2; i++ {        wg.Add(1)        go print(t, i, wg, mutex)    }    wg.Wait()}这是go的测试用例,并没有用main函数来执行,go的源码里面很多都是以这种形式写的测试用例。这里需要说明一下,go的协程需要达到真正的并发,需要加上runtime.GOMAXPROCS(runtime.NumCPU()),print函数里面mutex.Lock()注释了,打印的结果是        sync_test.go:12: routine i=0 start!        sync_test.go:12: routine i=1 start!        sync_test.go:14: routine i=1 end!        sync_test.go:14: routine i=0 end!协程0(暂且这么称呼)先执行print函数,但并没有先结束,我们看到协程1是先结束的,这个程序就有并发安全性的问题。如果需要解决这个问题,达到谁先进入公共代码区域,谁就先结束,只需要把print函数里面互斥锁Lock()和Unlock()打开即可,会看到如下打印信息。        sync_test.go:12: routine i=0 start!        sync_test.go:14: routine i=0 end!        sync_test.go:12: routine i=1 start!        sync_test.go:14: routine i=1 end!
读写锁验证结论:如果一个协程在读,其他协程不可以写,其他协程可以读。如果一个协程在写,任何协程都不可以读和写首先验证多个读func TestReadLock(t *testing.T) {    lock := new(sync.RWMutex)    go Read(lock, 1) //多个协程随便读,并不锁住    go Read(lock, 2)    time.Sleep(time.Second * 4)}func Read(lock *sync.RWMutex, i int) {    println(i, "read start")    lock.RLock()//读锁定    defer lock.RUnlock()//读解锁    println(i, "reading")    if i == 2 {        time.Sleep(3 * time.Second)    } else {        time.Sleep(1 * time.Second)    }    println(i, "read end")}打印信息如下:1 read start2 read start1 reading2 reading1 read end2 read end我们可以看出,协程1在没有read完之前,协程2还是可以读的,即验证了可以有多个读这次来验证第二个结论,有一个协程在读,另个协程能不能写呢?func TestWriteLock(t *testing.T) {    lock := new(sync.RWMutex)    go Read(lock, 2)    go Read(lock, 4)    // time.Sleep(1 * time.Second)    go Write(lock, 1) //如果在读,不可以写,可以读,如果在写,不可以写,不可以读    go Write(lock, 3)    time.Sleep(10 * time.Second)}func Read(lock *sync.RWMutex, i int) {    println(i, "read start")    lock.RLock()    defer lock.RUnlock()    println(i, "reading")    if i == 2 {        time.Sleep(3 * time.Second)    } else {        time.Sleep(1 * time.Second)    }    println(i, "read end")}func Write(lock *sync.RWMutex, i int) {    println(i, "write start")    lock.Lock()//写锁定    defer lock.Unlock()//写解锁    println(i, "writing")    time.Sleep(1 * time.Second)    println(i, "write end")}打印如下:2 read start2 reading3 write start1 write start4 read start2 read end3 writing3 write end4 reading4 read end1 writing1 write end我们看到,协程2号线进入读,协程3号写开始,但并没有writing,而是等到协程2read end之后才开始writing,协程2号在读的时候,协程4号开始读,但由于协程3号是在协程2号之后进入write start,所以协程2read end后是协程3号writing,直到结束,协程4号开始reading接下来验证最后一个结论我们把TestWriteLock函数里面的Write协程放到前面func TestWriteLock(t *testing.T) {    lock := new(sync.RWMutex)    go Write(lock, 1) //如果在读,不可以写,可以读,如果在写,不可以写,不可以读    go Write(lock, 3)    go Read(lock, 2)    go Read(lock, 4)    time.Sleep(10 * time.Second)}打印信息如下:1 write start1 writing4 read start2 read start3 write start1 write end2 reading4 reading4 read end2 read end3 writing3 write end好了,我们已经验证了我们最开始的结论,是不是很简单。
原创粉丝点击