Go互斥锁浅谈

生命不息,学习不止

  • 题外话
    • 互斥锁
    • 互斥锁 Mutex
    • 读写锁 RWMutex
  • 你以为结束了

题外话

清晨第一缕阳光打在我的脸上,我从我席梦思床垫上爬起,拿起我的iPhone56,定了一份加了三斤可食用金粉的麻辣烫,哎,又是乏味的一个早上…… 人生就是这么简简单单
话说今天的风是真的大,是真冷啊

Go互斥锁浅谈_第1张图片
废话不多说,上货
Go互斥锁浅谈_第2张图片

互斥锁

在并发的情况下,多个线程或协程同时其修改一个变量,使用锁能保证在某一时间内,只有一个协程或线程修改这一变量;
Go语言中 sync 包里提供了互斥锁 Mutex 和读写锁 RWMutex 用于处理并发过程中可能出现同时两个或多个协程(或线程)读或写同一个变量的情况,我们先来说说Mutex ;
举个例子
Go互斥锁浅谈_第3张图片

package main
import (
    "fmt"
    "time"
)
func main() {
    var count = 0
    for i := 0; i < 10; i++ {
        go func(idx int) {
            count += 1
            fmt.Println(count)
        }(i)
    }
    time.Sleep(time.Second)
}

我们使用 go 函数名( 参数列表 )方式开启一个新的运行期线程,goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
运行结果:
Go互斥锁浅谈_第4张图片
可以发现count并没有稳定的+1

协程的执行顺序大致如下所示:
从寄存器读取 count 的值;
然后做加法运算;
最后写到寄存器。

按照上面的顺序,假如有一个协程取得 count 的值为 3,然后执行加法运算,此时又有一个协程对 count 进行取值,得到的值同样是 count ,最终两个协程的返回结果是相同的。

而锁的概念就是,当一个协程正在处理 count 时将 count 锁定,其它协程需要等待该协程处理完成并将 count 解锁后才能再进行操作,也就是说同时处理 count 的协程只能有一个,从而避免上面示例中的情况出现。

互斥锁 Mutex

package main
import (
    "fmt"
    "sync"
    "time"
)
func main() {
    //声明一个互斥锁
    var lock sync.Mutex
    var count = 0
    for i := 0; i < 10; i++ {
        go func(idx int) {
            //在赋值前上锁
            lock.Lock()
            count += 1
            //赋值后解锁
            lock.Unlock()
            fmt.Println(count)
        }(i)
    }
     // 确保所有协程执行完
    time.Sleep(time.Second)
}

运行结果
Go互斥锁浅谈_第5张图片
需要注意的是一个互斥锁只能同时被一个 goroutine 锁定,其它 goroutine 将阻塞直到互斥锁被解锁

读写锁 RWMutex

读写锁有如下四个方法:
**写操作的锁定和解锁分别是func (*RWMutex) Lock和func (*RWMutex) Unlock;
读操作的锁定和解锁分别是func (RWMutex) Rlock和func (RWMutex) RUnlock。

读写锁的区别在于:
当有一个 goroutine 获得写锁定,其它无论是读锁定还是写锁定都将阻塞直到写解锁;
当有一个 goroutine 获得读锁定,其它读锁定仍然可以继续;
当有一个或任意多个读锁定,写锁定将等待所有读锁定解锁之后才能够进行写锁定。

所以说这里的读锁定(RLock)目的其实是告诉写锁定,有很多协程或者进程正在读取数据,写操作需要等它们读(读解锁)完才能进行写(写锁定)。

我们可以将其总结为如下三条:
同时只能有一个 goroutine 能够获得写锁定;
同时可以有任意多个 gorouinte 获得读锁定;
同时只能存在写锁定或读锁定(读和写互斥)。

// 调用事件
package main
import (
    "fmt"
    "math/rand"
    "sync"
)
var count int
var rw sync.RWMutex
func main() {
    ch := make(chan struct{}, 10)
    for i := 0; i < 5; i++ {
        go read(i, ch)
    }
    for i := 0; i < 5; i++ {
        go write(i, ch)
    }
    for i := 0; i < 10; i++ {
        <-ch
    }
}
func read(n int, ch chan struct{}) {
    rw.RLock()
    fmt.Printf("goroutine %d 进入读操作...\n", n)
    v := count
    fmt.Printf("goroutine %d 读取结束,值为:%d\n", n, v)
    rw.RUnlock()
    ch <- struct{}{}
}
func write(n int, ch chan struct{}) {
    rw.Lock()
    fmt.Printf("goroutine %d 进入写操作...\n", n)
    v := rand.Intn(1000)
    count = v
    fmt.Printf("goroutine %d 写入结束,新值为:%d\n", n, v)
    rw.Unlock()
    ch <- struct{}{}
}

Go互斥锁浅谈_第6张图片

你以为结束了

锁并不只有这些,底层原理也没有细说,锁的升级等等,等我再开一篇详细说说
Go互斥锁浅谈_第7张图片
大家看完发现有什么错误,写在下面吧!跟我黑虎阿福比划比划!
Go互斥锁浅谈_第8张图片

你可能感兴趣的:(Go,golang,开发语言,后端)