Go错误集锦 | 字符串格式化竟然能引起死锁
2023-06-13 09:16:47 时间
今天跟大家分享一个关于格式化字符串时造成的死锁现象及对应的解决方案。以便大家在今后的研发中可以避免类似情况的出现。
假设我们有以下Customer结构体,该结构体的方法可以被并发访问。我们使用sync.RWMutex来保护并发读写。然后我们实现一个UpdateAge方法来更新Customer的age值,同时检查该age如果是负数,则使用fmt.Errorf返回一个格式化的字符串错误。同时,Customer还实现了Stringer接口的String方法。代码如下:
type Customer struct {
mutex sync.RWMutex
id string
age int
}
func (c *Customer) UpdateAge(age int) error {
c.mutex.Lock()
defer c.mutex.Unlock()
if age < 0 {
return fmt.Errorf("age should be positive for customer %s", c)
}
c.age = age
return nil
}
func (c *Customer) String() string {
c.mutex.RLock()
defer c.mutex.RUnlock()
return fmt.Sprintf("id %s, age %d", c.id, c.age)
}
上述代码有什么问题吗?
问题在于当我们调用UpdateAge方法时,有可能会产生死锁。因为若age是负数,那么会返回一个错误,又因为在错误中使用了%s对结构体实例进行字符串输出,所以会调用Customer的String方法。但是在UpdateAge中已经获取了互斥锁,还没来得及释放,在String中的c.mutex.RLock()就获取不到锁,并阻塞在这里,这样就造成了死锁。
这种问题应该怎么解决?
一种方法就是改进互斥锁的限制区域。实际上,在UpdateAge中,我们是先加锁,然后再判断age是否是负数。我们可以将二者交换,先判断age是否为负数,然后再进行加锁。如下:
func (c *Customer) UpdateAge(age int) error {
if age < 0 {
return fmt.Errorf("age should be positive for customer %s", c)
}
c.mutex.Lock()
defer c.mutex.Unlock()
c.age = age
return nil
}
这样,当age是负数并返回格式化的字符串时,就不会产生锁竞争从而导致死锁的问题。以上案例,希望能够帮助大家在实际的研发过程中避免再踩相同的坑。
欢迎关注「Go学堂」,让知识活起来
相关文章
- Go Web---RPC
- go 对象json转map
- 如何优雅的通过Shell脚本一键部署GO项目到服务器 |Go主题月
- 2022-10-31:以下go语言代码输出什么?A:map[];B:nil;C:Panic;D:编译错误。 package main import “fmt“
- 2022-07-22:以下go语言代码输出什么?A:1;B:1.5;C:编译错误;D:1.49。package mainimpo
- 2022-09-10:以下go语言代码输出什么?A:编译错误;B:49.0;C:49。package mainimport (
- 2022-10-22:以下go语言代码输出什么?A:moonfdd1;B:编译错误;C:运行时 panic。package ma
- 开篇:为什么学习 Go 语言
- Go 数据结构和算法篇(二):栈
- Go - Web
- Go常见错误集锦之append操作slice时的副作用
- Go错误集锦 | 处理error时有哪些常见的陷阱
- 「Go」接口 interface: 一个案例说清用法和注意
- 【错误记录】Android Studio 中编写 Gradle 编译脚本时没有 Groovy 代码提示 ( Cannot find declaration to go to )
- Go-标准库-fmt(二)
- Go-包管理-go install
- Go-HTTP服务(三)
- Go 语言中的 Slice 陷阱:如何避免常见错误
- Go语言标准库强大
- Go_ go mod 命令解决墙的问题详解编程语言
- Go语言解码未知结构的JSON数据
- 程序Linux上运行Go语言程序的指南(linux运行go)
- 五种加速 Go 的特性
- 关键字在SQL Server中利用GO关键字实现更优化的操作(sqlserver中go)
- 一步一步学习Go读取Oracle数据库(go读取oracle)
- Go语句让Oracle数据库管理更方便(oracle go语句)