猿问

atomic.AddInt64 导致无效的内存地址或 nil 指针取消引用

在 struct panics 的字段上调用 atomic.AddInt64 invalid memory address or nil pointer dereference,但在我们重新排列字段顺序时不会;为什么?


使用这种类型:


type CountHandler struct {

    c     *RequestContext

    count int64

}

并且调用atomic.AddInt64(&countHandler.count, 1)(此时字段c为零)恐慌。但当我们将其重写为:


type CountHandler struct {

    count int64

    c     *RequestContext

}

错误消失。


我想应该是这样,因为 Go 以顺序方式将数据保存在内存中,并且达到一个nil值会破坏这个(字节)序列;但我想知道为什么又是这样,因为指针应该具有固定大小nil或其他值。


这是 Windows 上的 Go x86 1.4.2,完整的错误消息是:


2015/02/23 12:56:44 http: panic serving [::1]:51886: runtime error: invalid memory address or nil pointer dereference

goroutine 5 [running]:

net/http.func·011()

        c:/go/src/net/http/server.go:1130 +0xa8

sync/atomic.AddUint64(0x731144, 0x1, 0x0, 0x0, 0x263168)

        c:/go/src/sync/atomic/asm_386.s:118 +0xc

main.(*CountHandler).ServeHTTP(0x731140, 0x263180, 0x122f6380, 0x122f62a0)

        C:/Workshop/Devox/Workshop-Go/src/geoho/web/app/app.go:62 +0x42

github.com/julienschmidt/httprouter.func·001(0x263180, 0x122f6380, 0x122f62a0, 0x0, 0x0, 0x0)

        C:/Workshop/Devox/Workshop-Go/src/github.com/julienschmidt/httprouter/router.go:232 +0x4c

github.com/julienschmidt/httprouter.(*Router).ServeHTTP(0x122d5d20, 0x263180, 0x122f6380, 0x122f62a0)

        C:/Workshop/Devox/Workshop-Go/src/github.com/julienschmidt/httprouter/router.go:298 +0x141

net/http.serverHandler.ServeHTTP(0x122d2280, 0x263180, 0x122f6380, 0x122f62a0)

        c:/go/src/net/http/server.go:1703 +0x145

net/http.(*conn).serve(0x122e01e0)

        c:/go/src/net/http/server.go:1204 +0x9d8

created by net/http.(*Server).Serve

        c:/go/src/net/http/server.go:1751 +0x2ce


繁华开满天机
浏览 305回答 2
2回答

慕运维8079593

在第一种情况下,错误是由原子更新的字段没有正确对齐引起的。在 ARM 和 x86-32 上,调用者负责安排以原子方式访问的 64 位字的 64 位对齐。全局变量或分配的结构体或切片中的第一个字可以依赖于 64 位对齐。

繁星淼淼

如果您偶然发现此错误,这里有一些解决问题的技巧:如 OP 中所述,最简单的方法是将所有 64 位原子值放在结构的顶部:c := struct {    val   int64 // pos 0    val2  int64 // pos 8    valid bool  // pos 16}{val2: 1}fmt.Println(atomic.AddInt64(&c.val2, 1))如果您出于某种原因不想将此字段放在顶部,则始终可以_ [4]byte在 64 位字段上方放置 a以确保正确填充它。c := struct {    val   int64   // pos 0    valid bool    // pos 8    _     [4]byte // pos 9; compiler adds additional [3]byte at pos 13 for alignment    val2  int64   // pos 16, correctly aligned}{val2: 1}fmt.Println(atomic.AddInt64(&c.val2, 1)) // => 2请注意,如果字段已经对齐,这将不起作用;相反,如果它之前没有恐慌,它现在就会恐慌。c := struct {    val   int64   // pos 0    _     [4]byte // pos 8; compiler adds no padding    val2  int64   // pos 12, not a multiple of 8!}{val2: 1}fmt.Println(atomic.AddInt64(&c.val2, 1)) // => runtime error: invalid memory address [...]您还可以依赖 64 位元素切片中的第一个元素将正确对齐的行为:c := struct {    val   int64    valid bool    val2  []int64}{val2: []int64{1}}fmt.Println(atomic.AddInt64(&c.val2[0], 1))请注意,这不适用于数组,因为它们的值直接存储在结构中,而不是像切片数据那样存储在堆中。您可以使用的最后一个技巧是将结构中的字段声明为指向int64;的指针。如果int64它指向的是对齐的,那么它会顺利运行。c := struct {    val   int64    valid bool    val2  *int64}{val2: new(int64)}fmt.Println(atomic.AddInt64(c.val2, 1))如果你不想弄脏你的手sync/atomic,请记住,这sync.Mutex是一个比处理原子更清晰、更容易理解的解决方案。
随时随地看视频慕课网APP

相关分类

Go
我要回答