品牌网站 响应式网站,wordpress oss静态,百度网站考核期,wordpress 相册模板本期分享#xff1a;
1.循环依赖导致栈溢出
2.无法捕获子协程的panic 循环依赖导致栈溢出
在Go语言开发中#xff0c;我们经常会遇到结构体之间需要相互引用的情况。当两个结构体直接或间接地相互包含对方作为自己的字段时#xff0c;就会形成循环依赖。
但是在Go语言中…本期分享
1.循环依赖导致栈溢出
2.无法捕获子协程的panic 循环依赖导致栈溢出
在Go语言开发中我们经常会遇到结构体之间需要相互引用的情况。当两个结构体直接或间接地相互包含对方作为自己的字段时就会形成循环依赖。
但是在Go语言中直接进行结构体的相互引用会默认不符合语法因此我们就使用接口进行引用。
代码示例
结构体A
type A struct {Name stringHi Hi
}type Say interface {Say()
}func (a *A) Say() {fmt.Println(a.Name, say Hi)
}func NewA(name string) *A {return A{Name: name,Hi: NewB(B),}
}结构体B
type B struct {Name stringSay Say
}type Hi interface {Hi()
}func (b *B) Hi() {fmt.Println(Hi , b.Name)
}func NewB(name string) *B {return B{Name: name,Say: NewA(A),}
}当调用NewA(“A”)时程序会立即崩溃并报错
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow错误原因分析 无限递归初始化NewA调用NewBNewB又调用NewA从而形成无限循环调用链。 栈空间耗尽每次函数调用都会占用栈空间无限递归导致栈空间被耗尽最终触发栈溢出错误
解决方案
方案1打破初始化循环
func NewA(name string) *A {b : B{Name: B} // 先创建B实例a : A{Name: name,Hi: b, // 直接赋值}b.Say a // 后设置B的Say字段return a
}方案2使用接口延迟设置
type A struct {Name stringHi Hi // 使用接口类型
}type B struct {Name stringSay Say // 使用接口类型
}// 初始化时先创建实例后设置字段
a : A{Name: A}
b : B{Name: B}
a.Hi b
b.Say a方案3重新设计结构
考虑是否真的需要双向依赖可以将共用逻辑提取到第三个结构体。
Go语言中的循环依赖问题看似简单但可能导致严重的运行时错误。通过本文的分析和解决方案我们可以更安全地处理对象间的复杂关系
无法捕获子协程的panic
在Go语言中父协程默认情况下不能直接捕获子协程的panic。这是由Go的并发模型和goroutine的设计决定的
func Run() {defer func() {if r : recover(); r ! nil {log.Printf(ping panic: %v, r) // 这个recover只能捕获当前goroutine的panic}}()go func() {panic(panic)}()time.Sleep(time.Second * 3)
}原因如下 独立的执行栈每个goroutine都有自己的调用栈panic和recover机制是基于当前goroutine的调用栈的 设计哲学Go的设计是让每个goroutine自己处理自己的错误而不是由父goroutine来管理 并发安全如果允许跨goroutine捕获panic会导致复杂的并发问题
正确写法
func Run() {go func() {defer func() {if r : recover(); r ! nil {log.Printf(ping panic: %v, r)}}()panic(panic)}()time.Sleep(time.Second * 3)
}本篇结束~