Skip to content
zig 版本:0.12.0

原子操作

原子操作是在多线程环境中非常重要的一个概念,原子操作是指一个或一系列的操作,它们作为一个整体来执行,中间不会被任何其他的操作打断。这意味着原子操作要么全部完成,要么全部不完成,不会出现只完成部分操作的情况。

目前 zig 提供了一些内建函数来进行原子操作,并且提供了 std.atomic 命名空间来实现内存排序、原子数据结构。

内建函数

在讲述下列的内建函数前,我们需要了解一下前置知识:

原子操作的顺序级别:为了实现性能和必要保证之间的平衡,原子性分为六个级别。它们按照强度顺序排列,每个级别都包含上一个级别的所有保证。

关于原子顺序六个级别的具体说明,见 LLVM

@atomicLoad

函数原型:

zig
@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T

用于某个类型指针进行原子化的读取值。

@atomicStore

函数原型:

zig
`@atomicStore(comptime T: type, ptr: *T, value: T, comptime ordering: builtin.AtomicOrder) void`

用于对某个类型指针进行原子化的赋值。

@atomicRmw

函数原型:

zig
@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T

用于原子化的修改值并返回修改前的值。

其还支持九种操作符,具体 见此

@cmpxchgWeak

函数原型:

zig
@cmpxchgWeak(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T

弱原子的比较与交换操作,如果目标指针是给定值,那么赋值为参数的新值,并返回null,否则仅读取值返回。

@cmpxchgStrong

函数原型:

zig
@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T

强原子的比较与交换操作,如果目标指针是给定值,那么赋值为参数的新值,并返回null,否则仅读取值返回。

@fence

函数原型:

zig
@fence(order: AtomicOrder) void

用于创建一个内存屏障,防止某些类型的内存重新排序,具体细节可以查看内存屏障的相关信息。

std.atomic

原子数据结构

可以使用 std.atomic.Value 包裹某种类型获取到一个原子数据结构。

示例:

zig
const RefCount = struct {
    count: Value(usize),
    dropFn: *const fn (*RefCount) void,

    const RefCount = @This();

    fn ref(rc: *RefCount) void {
        // No ordering necessary; just updating a counter.
        _ = rc.count.fetchAdd(1, .Monotonic);
    }

    fn unref(rc: *RefCount) void {
        // Release ensures code before unref() happens-before the
        // count is decremented as dropFn could be called by then.
        if (rc.count.fetchSub(1, .Release) == 1) {
            // Acquire ensures count decrement and code before
            // previous unrefs()s happens-before we call dropFn
            // below.
            // Another alternative is to use .AcqRel on the
            // fetchSub count decrement but it's extra barrier in
            // possibly hot path.
            rc.count.fence(.Acquire);
            (rc.dropFn)(rc);
        }
    }

    fn noop(rc: *RefCount) void {
        _ = rc;
    }
};

var ref_count: RefCount = .{
    .count = Value(usize).init(0),
    .dropFn = RefCount.noop,
};
ref_count.ref();
ref_count.unref();

spinLoopHint 自旋锁

std.atomic.spinLoopHint

向处理器发出信号,表明调用者处于忙等待自旋循环内。

示例:

zig
test spinLoopHint {
    for (0..10) |_| {
        spinLoopHint();
    }
}

TODO:增加更多的讲解,例如使用示例,原子级别讲解等!