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

Pointers atomic.AddInt64导致无效内存地址或零指针取消引用,pointers,go,memory-management,atomic,Pointers,Go,Memory Management,Atomic,在结构的字段上调用atomic.AddInt64会导致内存地址无效或零指针取消引用,但在重新排列字段顺序时不会这样做;为什么? 使用此类型: type CountHandler struct { c *RequestContext count int64 } 调用atomic.AddInt64(&countHandler.count,1)(此时字段c为零)会造成恐慌。但当我们将其改写为: type CountHandler struct { count int6

在结构的字段上调用atomic.AddInt64会导致内存地址无效或零指针取消引用,但在重新排列字段顺序时不会这样做;为什么?

使用此类型:

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
整个源代码是(此代码是错误的,我只是想学习
alice
):


在第一种情况下,故障是由于原子更新字段未正确执行而导致的

在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))
无论出于何种原因,如果您不想将此字段置于顶部,您可以始终在64位字段上方放置一个
[4]字节
,以确保其填充正确

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
,请记住,这是一个比处理原子学更干净、更容易理解的解决方案。

对我来说似乎是一个bug。我用了你的代码,在Linux上运行得很好。在操场上也一样。看看这个:这段代码在你的Windows机器上会崩溃吗?是的,它应该能工作。请发布更多代码,例如如何创建
countHandler
变量。请瞄准目标。谢谢;我已经添加了代码。哦,很好的捕获,我错过了x86部分!
c := struct {
    val   int64
    valid bool
    val2  *int64
}{val2: new(int64)}
fmt.Println(atomic.AddInt64(c.val2, 1))