概念定义
缓存一致性按行(通常 64 字节)运作,而非按变量。两个线程写同一行内的不同变量时,每次写都让另一核的副本失效。变量逻辑私有、物理共享,性能表现出争用般的数倍劣化——且只有剖析才看得见。
工作机制
每次写入,核心都要独占该行并使他核副本失效;上一个写者下次再写又要重新取回,行就这样乒乓。检测靠聚集在同一地址段的缓存未命中计数,或 perf c2c 高亮被多线程命中的行。修复是填充或重排,让每个热写者独占整行。
使用场景
每线程统计结构体、环形缓冲头部、任务队列元数据,以及“每个 worker 一个字段且连续排布”的任何结构。把热可变字段按行对齐,或按写者而非按类别分组。
常见误区
“只有共享数据才有争用”错在单位:一致性按行不按变量。线程越多,行上的失效流量越倍增。盲目填充会让冷数据白白吃内存与缓存密度——先测量。GC 语言布局不受控:改用带填充槽的结构数组或堆外结构。一次无害的重构把两个热字段并进同一结构体,伪共享就会悄悄现身。
代码示例
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
type Counters struct {
requests atomic.Int64
errors atomic.Int64
bytes atomic.Int64
}
func (c *Counters) IncRequest() { c.requests.Add(1) }
func (c *Counters) IncError() { c.errors.Add(1) }
func (c *Counters) AddBytes(n int64) { c.bytes.Add(n) }
func (c *Counters) Snapshot() (req, errs, transferred int64) {
return c.requests.Load(), c.errors.Load(), c.bytes.Load()
}
type RateCalculator struct {
lastValue atomic.Int64
lastTime atomic.Int64
}
func (r *RateCalculator) Rate(now time.Time, current int64) float64 {
prevTime := r.lastTime.Load()
prevValue := r.lastValue.Load()
if prevTime == 0 {
r.lastTime.Store(now.Unix())
r.lastValue.Store(current)
return 0
}
elapsed := now.Unix() - prevTime
if elapsed <= 0 {
return 0
}
rate := float64(current-prevValue) / float64(elapsed)
r.lastTime.Store(now.Unix())
r.lastValue.Store(current)
return rate
}
func main() {
counters := new(Counters)
rate := new(RateCalculator)
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
counters.IncRequest()
if n%50 == 0 {
counters.IncError() // 模拟约百分之二的错误率
}
counters.AddBytes(512)
}(i)
}
wg.Wait()
req, errs, transferred := counters.Snapshot()
fmt.Println(req, errs, transferred, rate.Rate(time.Now(), req))
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"sync"
)
var bufferPool = sync.Pool{
New: func() any { return new(bytes.Buffer) },
}
func EncodeJSON(value any) ([]byte, error) {
buffer := bufferPool.Get().(*bytes.Buffer)
buffer.Reset()
defer bufferPool.Put(buffer)
if err := json.NewEncoder(buffer).Encode(value); err != nil {
return nil, err
}
out := make([]byte, buffer.Len())
copy(out, buffer.Bytes())
return out, nil
}
func main() {
type payload struct {
ID int `json:"id"`
Name string `json:"name"`
}
encoded, err := EncodeJSON(payload{ID: 1, Name: "Ada"})
if err != nil {
panic(err)
}
fmt.Print(string(encoded))
fmt.Println(bytes.Equal(encoded, []byte("{\"id\":1,\"name\":\"Ada\"}\n")))
}