← 中文资源

简体中文版本

内存栅栏与屏障

内存栅栏约束指令重排与跨核写可见性,提供无锁代码在弱序 CPU 上正确组合所需的顺序保证。

阅读英文原文

概念定义

核心会缓冲并重排内存操作,编译器也会重排。两个线程通过内存通信时,必须就“写何时可见、以什么顺序可见”达成一致——栅栏就是在指令流里画下这些线。

工作机制

store 上的 release 顺序保证之前的写在释放写落地前可见;load 上的 acquire 保证后续读能看到匹配 release 之前发布的一切;store-load 全栅栏支撑顺序一致原子操作。互斥锁、channel、带顺序参数的原子操作这些高层构造最终都编译为这些原语。

使用场景

写无锁队列、发布共享对象的初始化、理解原子 API 的 memory_order 参数。多数开发者经由这些 API 间接消费栅栏,而不是直接发射。

常见误区

“现代 CPU 不需要栅栏”不成立:弱序架构依旧重排,带顺序语义的原子已隐式内嵌栅栏。x86 的强序会掩盖 bug,ARM 与 RISC-V 上原形毕露——必须在弱序硬件或模拟器上测试。双重检查锁是经典反例:release/acquire 位置差一点就碎。栅栏密集的热路径会实打实拖吞吐,先选高层原语。栅栏只管顺序,未同步的冲突访问仍是数据竞争。

代码示例

#!/usr/bin/env bash
set -euo pipefail

lock_file=${1:-"${TMPDIR:-/tmp}/devexamples-backup.lock"}
max_wait=${LOCK_WAIT_SECONDS:-5}
work_file=""

cleanup() {
    local status=$?
    if [[ -n "$work_file" && -f "$work_file" ]]; then
        rm -f -- "$work_file"
    fi
    flock -u 9 2>/dev/null || true
    exit "$status"
}
trap cleanup EXIT

exec 9>"$lock_file"

if ! flock -w "$max_wait" 9; then
    printf 'could not acquire %s within %ss\n' "$lock_file" "$max_wait" >&2
    exit 75
fi

printf 'lock acquired by pid %d\n' "$"

work_file="$(mktemp "${TMPDIR:-/tmp}/backup.XXXXXX")"
printf 'job %d started at %s\n' "$" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$work_file"

sleep "${JOB_SECONDS:-1}"
cat -- "$work_file"
printf 'job %d finished; lock released on exit\n' "$"
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))
}