清晨第一缕阳光打在我的脸上,我从我席梦思床垫上爬起,拿起我的iPhone56,定了一份加了三斤可食用金粉的麻辣烫,哎,又是乏味的一个早上…… 人生就是这么简简单单
话说今天的风是真的大,是真冷啊
在并发的情况下,多个线程或协程同时其修改一个变量,使用锁能保证在某一时间内,只有一个协程或线程修改这一变量;
Go语言中 sync 包里提供了互斥锁 Mutex 和读写锁 RWMutex 用于处理并发过程中可能出现同时两个或多个协程(或线程)读或写同一个变量的情况,我们先来说说Mutex ;
举个例子
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 运行时进行管理的。
运行结果:
可以发现count并没有稳定的+1
协程的执行顺序大致如下所示:
从寄存器读取 count 的值;
然后做加法运算;
最后写到寄存器。
按照上面的顺序,假如有一个协程取得 count 的值为 3,然后执行加法运算,此时又有一个协程对 count 进行取值,得到的值同样是 count ,最终两个协程的返回结果是相同的。
而锁的概念就是,当一个协程正在处理 count 时将 count 锁定,其它协程需要等待该协程处理完成并将 count 解锁后才能再进行操作,也就是说同时处理 count 的协程只能有一个,从而避免上面示例中的情况出现。
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)
}
运行结果
需要注意的是一个互斥锁只能同时被一个 goroutine 锁定,其它 goroutine 将阻塞直到互斥锁被解锁
读写锁有如下四个方法:
**写操作的锁定和解锁分别是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{}{}
}
锁并不只有这些,底层原理也没有细说,锁的升级等等,等我再开一篇详细说说
大家看完发现有什么错误,写在下面吧!跟我黑虎阿福比划比划!