程序包 "fmt" 运行时问题
Package "fmt" runtime issue
我遇到一个看似简单但无法重现的问题,因此无法解释。
这个问题出现在生产中,神秘的是它很少发生(这就是为什么我无法重现它),这可能是一个因素,我无法举出例子,但这是上下文:
type MyType struct {
Field1 string
Field2 int
Field3 time.Time
Field4 []float64
// No pointers fields
}
func main() {
var MyChan = make(chan interface{})
go func() {
// This routine is reading and parsing messages from a WS dial and writing it in a channel
// There is 2 only possible answer in the channel : a string, or a "MyType" like (with more fields, but no pointers in)
var Object MyType
Object.Field1 = "test"
// ..
MyChan <- Object
}()
go func() {
// This routine is sending a message to a WS dial and wait for the answer in a channel fed by another routine :
var Object interface{}
go func(Get *interface{}) {
*Get = <- MyChan
} (&Object)
for Object == nil {
time.Sleep(time.Nanosecond * 1)
}
log.Println(fmt.Sprint(Object)) // Panic here from the fmt.Sprint() func
}()
}
紧急堆栈跟踪:
runtime error: invalid memory address or nil pointer dereference
panic(0x87a840, 0xd0ff40)
X:/Go/GoRoot/src/runtime/panic.go:522 +0x1b5
reflect.Value.String(0x85a060, 0x0, 0xb8, 0xc00001e500, 0xc0006f1a80)
X:/Go/GoRoot/src/reflect/value.go:1781 +0x45
fmt.(*pp).printValue(0xc006410f00, 0x85a060, 0x0, 0xb8, 0x76, 0x1)
X:/Go/GoRoot/src/fmt/print.go:747 +0x21c3
fmt.(*pp).printValue(0xc006410f00, 0x8ed5c0, 0x0, 0x99, 0x76, 0x0)
X:/Go/GoRoot/src/fmt/print.go:796 +0x1b52
fmt.(*pp).printArg(0xc006410f00, 0x8ed5c0, 0x0, 0x76)
X:/Go/GoRoot/src/fmt/print.go:702 +0x2ba
fmt.(*pp).doPrint(0xc006410f00, 0xc0006f22a0, 0x1, 0x1)
X:/Go/GoRoot/src/fmt/print.go:1147 +0xfd
fmt.Sprint(0xc0006f22a0, 0x1, 0x1, 0x0, 0x0)
X:/Go/GoRoot/src/fmt/print.go:250 +0x52
Go 版本:1.12.1 windows/amd64
感谢您的宝贵时间,希望有人能向我解释一下哪里出了问题。
你这里有一个数据竞争:
var Object interface{}
go func(Get *interface{}) {
*Get = <- MyChan
} (&Object)
for Object == nil {
time.Sleep(time.Nanosecond * 1)
}
写变量 Object
的 goroutine 没有使用锁:它从通道接收,当它得到一个值时,它把它写到 *Get
where Get == &Object
, 从而写入Object
.
的接口值
同时,goroutine 运行 for
循环从 Object
读取,检查是否为零。它在不使用锁的情况下读取,因此它可以读取部分写入的值。
实际发生的是部分写入的值不是零,而没有设置整个值。所以 for
循环停止循环,代码进行到下一行:
log.Println(fmt.Sprint(Object)) // Panic here from the fmt.Sprint() func
因为 Object
只是 部分 写入,访问它的值会产生不可预知的结果——但在这种情况下,它会产生恐慌。 (特别是接口的type字段已经设置了,但是value字段还是0,真正的panic是从这行开始的src/reflect/value.go
:
return *(*string)(v.ptr)
v.ptr
尚未设置。)
不清楚为什么要记录值,也不清楚为什么要使用共享内存进行通信,但是如果您这样做,则需要锁定。不这样做通常更明智。另见 to .
(或者,更简单地说,为什么不直接使用 Object := <-MyChan
来代替整个 goroutine-and-spin?)
我遇到一个看似简单但无法重现的问题,因此无法解释。
这个问题出现在生产中,神秘的是它很少发生(这就是为什么我无法重现它),这可能是一个因素,我无法举出例子,但这是上下文:
type MyType struct {
Field1 string
Field2 int
Field3 time.Time
Field4 []float64
// No pointers fields
}
func main() {
var MyChan = make(chan interface{})
go func() {
// This routine is reading and parsing messages from a WS dial and writing it in a channel
// There is 2 only possible answer in the channel : a string, or a "MyType" like (with more fields, but no pointers in)
var Object MyType
Object.Field1 = "test"
// ..
MyChan <- Object
}()
go func() {
// This routine is sending a message to a WS dial and wait for the answer in a channel fed by another routine :
var Object interface{}
go func(Get *interface{}) {
*Get = <- MyChan
} (&Object)
for Object == nil {
time.Sleep(time.Nanosecond * 1)
}
log.Println(fmt.Sprint(Object)) // Panic here from the fmt.Sprint() func
}()
}
紧急堆栈跟踪:
runtime error: invalid memory address or nil pointer dereference
panic(0x87a840, 0xd0ff40)
X:/Go/GoRoot/src/runtime/panic.go:522 +0x1b5
reflect.Value.String(0x85a060, 0x0, 0xb8, 0xc00001e500, 0xc0006f1a80)
X:/Go/GoRoot/src/reflect/value.go:1781 +0x45
fmt.(*pp).printValue(0xc006410f00, 0x85a060, 0x0, 0xb8, 0x76, 0x1)
X:/Go/GoRoot/src/fmt/print.go:747 +0x21c3
fmt.(*pp).printValue(0xc006410f00, 0x8ed5c0, 0x0, 0x99, 0x76, 0x0)
X:/Go/GoRoot/src/fmt/print.go:796 +0x1b52
fmt.(*pp).printArg(0xc006410f00, 0x8ed5c0, 0x0, 0x76)
X:/Go/GoRoot/src/fmt/print.go:702 +0x2ba
fmt.(*pp).doPrint(0xc006410f00, 0xc0006f22a0, 0x1, 0x1)
X:/Go/GoRoot/src/fmt/print.go:1147 +0xfd
fmt.Sprint(0xc0006f22a0, 0x1, 0x1, 0x0, 0x0)
X:/Go/GoRoot/src/fmt/print.go:250 +0x52
Go 版本:1.12.1 windows/amd64
感谢您的宝贵时间,希望有人能向我解释一下哪里出了问题。
你这里有一个数据竞争:
var Object interface{} go func(Get *interface{}) { *Get = <- MyChan } (&Object) for Object == nil { time.Sleep(time.Nanosecond * 1) }
写变量 Object
的 goroutine 没有使用锁:它从通道接收,当它得到一个值时,它把它写到 *Get
where Get == &Object
, 从而写入Object
.
同时,goroutine 运行 for
循环从 Object
读取,检查是否为零。它在不使用锁的情况下读取,因此它可以读取部分写入的值。
实际发生的是部分写入的值不是零,而没有设置整个值。所以 for
循环停止循环,代码进行到下一行:
log.Println(fmt.Sprint(Object)) // Panic here from the fmt.Sprint() func
因为 Object
只是 部分 写入,访问它的值会产生不可预知的结果——但在这种情况下,它会产生恐慌。 (特别是接口的type字段已经设置了,但是value字段还是0,真正的panic是从这行开始的src/reflect/value.go
:
return *(*string)(v.ptr)
v.ptr
尚未设置。)
不清楚为什么要记录值,也不清楚为什么要使用共享内存进行通信,但是如果您这样做,则需要锁定。不这样做通常更明智。另见
(或者,更简单地说,为什么不直接使用 Object := <-MyChan
来代替整个 goroutine-and-spin?)