[关闭]
@sheepbao 2018-08-01T16:53:24.000000Z 字数 2031 阅读 1940

在32位系统中使用64位原子操作的坑

先来个例子

  1. type Y struct {
  2. a bool
  3. v uint64
  4. }
  5. func TestAtomicY(t *testing.T) {
  6. var y Y
  7. atomic.AddUint64(&y.v, 1) // panic in 32bit system
  8. }

在上面的例子中,如果在64位系统中运行是没问题的的,但是在32位系统中会panic。

为何会 Panic ?

这个简单回答可以查看,go官方的文档atomic-pkt-note的内容:

  1. Bugs
  2. On x86-32, the 64-bit functions use instructions unavailable before the Pentium MMX.
  3. On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
  4. On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.

意思就是你如果要在32位系统中用64位的原子操作,必须要自己保证64位对齐,也就是8字节对齐。
如果要看汇编怎么判断的可以查看atomic·Xadd64

如果需要在一个struct中维护多个64位字段,且都需要原子操作,怎么办?

最简单的办法就是将所有64位字段放在struct的头部,这样就可以保证8字节对齐,如果你把这个struct嵌入在别的结构体,也要记得嵌入到头部。

一些例子

  1. package aotmic_test
  2. import (
  3. "log"
  4. "sync/atomic"
  5. "testing"
  6. "unsafe"
  7. )
  8. type X struct {
  9. v uint64
  10. x uint64
  11. a bool
  12. z uint64
  13. y uint32
  14. }
  15. func TestAtomic(t *testing.T) {
  16. var x X
  17. log.Printf("x.a=%p, offset=%d, alig=%d", &x.a, unsafe.Offsetof(x.a), unsafe.Alignof(x.a))
  18. log.Printf("x.v=%p, offset=%d, alig=%d", &x.v, unsafe.Offsetof(x.v), unsafe.Alignof(x.v))
  19. log.Printf("x.x=%p, offset=%d, alig=%d", &x.x, unsafe.Offsetof(x.x), unsafe.Alignof(x.x))
  20. log.Printf("x.y=%p, offset=%d, alig=%d", &x.y, unsafe.Offsetof(x.y), unsafe.Alignof(x.y))
  21. log.Printf("x.z=%p, offset=%d, alig=%d", &x.z, unsafe.Offsetof(x.z), unsafe.Alignof(x.z))
  22. log.Printf("x.v=%p", &x.v)
  23. atomic.AddUint64(&x.z, 1) // panic
  24. }
  25. type Y struct {
  26. a bool
  27. X
  28. }
  29. func TestAtomicY(t *testing.T) {
  30. var y Y
  31. x := y.X
  32. atomic.AddUint64(&x.v, 1)
  33. atomic.AddUint64(&y.X.v, 1) // panic
  34. }
  35. type Y2 struct {
  36. X
  37. a bool
  38. }
  39. func TestAtomicY2(t *testing.T) {
  40. y := &Y2{}
  41. atomic.AddUint64(&y.X.v, 1)
  42. }
  43. type Temp struct {
  44. A byte
  45. B [2]byte
  46. C int64
  47. }
  48. func TestAtomicTemp(t *testing.T) {
  49. var x Temp
  50. log.Printf("sizof=%d", unsafe.Sizeof(x))
  51. log.Printf("x.A=%p, offset=%d, alig=%d", &x.A, unsafe.Offsetof(x.A), unsafe.Alignof(x.A))
  52. log.Printf("x.B=%p, offset=%d, alig=%d", &x.B, unsafe.Offsetof(x.B), unsafe.Alignof(x.B))
  53. log.Printf("x.C=%p, offset=%d, alig=%d", &x.C, unsafe.Offsetof(x.C), unsafe.Alignof(x.C))
  54. }

参考链接

https://go101.org/article/memory-layout.html

https://github.com/golang/go/issues/5278

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注