2

Go 语言的读写锁真的是读不阻塞么

 3 years ago
source link: https://www.zenlife.tk/go-read-mutex-block.md
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Go 语言的读写锁真的是读不阻塞么

2021-08-20

发现一个神奇的问题:

package main

import (
	"fmt"
	"time"
	"sync"
)

func main() {
	var mu sync.RWMutex

	go func() {
		mu.RLock()
		time.Sleep(10*time.Second)
		mu.RUnlock()
	}()

	time.Sleep(100*time.Millisecond)

	// go func() {
	// 	mu.Lock()
	// 	mu.Unlock()
	// }()

	time.Sleep(100*time.Millisecond)

	start := time.Now()
	fmt.Println("before get read block")
	mu.RLock()
	fmt.Println("inside read load no block", time.Since(start))
	mu.RUnlock()
}

这一段代码,如果去掉注释的那一段...最后面的读锁会阻塞住!

也就是说,只存在读锁的时候,读读不阻塞。 但是当存在写锁的时候,读锁能阻塞读锁...这跟我之前的认知不太一样,用了这么久 Go 我居然都不知道自己的理解行为一直有问题。

查了一下官方的标准库文档是这么说的:

If a goroutine holds a RWMutex for reading and another goroutine might call Lock, no goroutine should expect to be able to acquire a read lock until the initial read lock is released.

这也就是上面代码看到的现象了:一个 goroutine 持有 RWMutex 的读锁,另一个 goroutine 调用写锁的时候,没有其它 goroutine 可以在最初的读锁释放之前,再获取到读锁。

-----------------

说说我怎么遇到这个问题的。弄了一个 map 容器,里面有放了地址到连接的映射。如果连接闲置了,就去回收掉。 用了一个读写锁,有 goroutine 在拿 map 里面的连接出去使用时,加上读锁。 回收不活跃连接时,加上写锁。 持有读锁之后,去发请求的操作有可能会卡住,但预期是不影响其它的连接继续的使用,或者说只是这一个地址的连接卡住。然而... duang! 当回收连接那个锁开始调用,哪怕还没有持有到写锁,读读也相互 block 了!

好啦,以后使用读写锁的代码,需要更加注意,读读也是可能阻塞的。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK