RWMutex không phải bản nâng cấp mặc định của Mutex. Nó phức tạp hơn: RLock/RUnlock phải cập nhật bộ đếm reader bằng thao tác atomic dùng chung. Khi critical section rất ngắn (đọc một field, tra một map nhỏ), chi phí đó lấn át phần song song thu được, và trên máy nhiều nhân các reader còn tranh cùng một cacheline. Với workload đó Mutex thường nhanh hơn.
RWMutex chỉ thắng rõ khi tỉ lệ đọc áp đảo và mỗi lần đọc đủ lâu để nhiều reader thực sự chồng lấn.
Về RLock lồng nhau: tài liệu sync viết rõ rằng nếu một goroutine đang giữ read lock và một goroutine khác gọi Lock, thì không goroutine nào được kỳ vọng lấy thêm read lock cho tới khi read lock đầu tiên nhả — điều này cấm read lock đệ quy.
Lý do là chống đói writer: khi có writer đang chờ, mọi RLock mới đều bị chặn, nếu không writer sẽ không bao giờ tới lượt. Hệ quả:
func (c *Cache) Get(k string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.resolve(k) // resolve() calls c.mu.RLock() again -> deadlock
}Goroutine này giữ read lock, gọi RLock lần hai, gặp writer đang chờ ở giữa → nó chờ writer, writer chờ nó nhả. Deadlock, và chỉ xuất hiện khi có ghi xen vào nên hay lọt qua test.
Xử lý: tách hàm thành phiên bản xxxLocked() giả định đã cầm khóa, không tự lấy lại. Với dữ liệu gần như chỉ đọc, cân nhắc atomic.Value/copy-on-write hoặc sync.Map thay vì tăng độ phức tạp của lock.