Lock
go语言中有两种锁:互斥锁和读写锁
互斥锁Mutex
常用于对并发资源的限制
如果不加锁,在协程中并发执行,最后结果可能非预期:
func TestLock(t *testing.T) {
counter := 0
for i := 0; i < 500; i++ {
go func() {
counter++
}()
}
t.Logf("counter %d\n", counter) // 结果几乎都不是500
}
使用Mutex加锁后执行:
func TestMutexLock(t *testing.T) {
counter := 0
var mut sync.Mutex
for i := 0; i < 500; i++ {
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
counter++
}()
}
time.Sleep(time.Second * 1) // 这里不加sleep结果不一定正确
t.Logf("counter %d\n", counter) // 当sleep 1s且循环次数不算特别大时,结果是正确的
}
会注意到,使用Mutex加锁后,在最后输出结果前sleep了1s,如果不执行sleep,结果大概率是不正确的,为什么呢?
因为主协程可能执行得比较快,导致还没计算出真正的结果就已经输出,因此sleep
但这里可以提出一个新的问题,如何界定这个sleep的时间?有没有可能sleep设置得不够?因此引出新的方式WaitGroup
WaitGroup
func TestMutexWaitGroup(t *testing.T) {
counter := 0
var mut sync.Mutex
var wg sync.WaitGroup
for i := 0; i < 500; i++ {
wg.Add(1)
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
counter++
wg.Done()
}()
}
wg.Wait()
t.Logf("counter %d\n", counter) // 结果几乎都不是500
}
读写锁RWMutex
1 RWLock读是非互斥锁,写是互斥锁
2 读写锁的读锁可以重入,在已经有读锁的情况下,可以任意加锁
3 在读锁没有全部解锁的情况下,写操作会阻塞直到所有读锁解锁
4 写锁定的情况下,其他协程的读写都会被阻塞,直到写锁解锁
5 读写锁本身是互斥的,读需要写锁都解锁,写需要读锁都解锁
channel
无buffer的channel
func server() string {
time.Sleep(time.Second * 2)
fmt.Println("run server...")
return "server"
}
func serverChan() chan string {
chanTest := make(chan string)
for i := 0; i <= 100; i++ {
go func() {
ret := server()
chanTest <- ret
}()
}
return chanTest
}
func TestChannel(t *testing.T) {
retChan := serverChan()
t.Log(<-retChan)
}
有buffer的channel
以上代码,make chan的时候提供buffer长度:
chanTest := make(chan string)
select
select中的case表示随机取到其中一个,当包含time.After时,会看是否到达设置的时间,到达则表示超时,执行对应代码
多渠道选择
多个渠道,随机执行:
func TestSelectChan(t *testing.T) {
retCh1 := serverChan()
retCh2 := anotherServerChan()
time.Sleep(time.Second * 2)
select {
case ret := <-retCh1:
t.Logf("ret: %v\n", ret)
case ret := <-retCh2:
t.Logf("ret: %v\n", ret)
case <-time.After(time.Second * 1):
t.Log("break......")
}
}
超时控制
上文中的case <-time.After(time.Second * 1)
即为超时控制
channel的关闭与广播
向关闭的channel发送数据,会导致panic:
func closeChan() chan string {
chanTest := make(chan string)
for i := 0; i <= 10; i++ {
go func() {
chanTest <- "aaa"
}()
}
close(chanTest)
return chanTest
}
func TestSendCloseToChannelPanic(t *testing.T) {
closeChan()
}
事实上,以上代码看起来好像是最后才关闭的chan,但实际上协程可能没执行完,当for循环的次数足够多,几乎都会出现以下报错:
panic: send on closed channel
对于此案例来说,要想解决问题,可以尝试之前用过的加锁方式: