概念定义
CAS 检查的是“值相等”,不是“没变过”。世界从 A 到 B 再回到 A 时,CAS 会当作世界静止不动。正确性取决于其间发生了什么:被释放又被重分配为不同逻辑元素的节点,让指针相等失去意义。
工作机制
经典栈弹出场景:线程读到栈顶 A;另一线程弹出 A、压入 B、再压回 A——同一节点。第一个线程的 CAS 成功并顺着 A 的 next 链接,而它可能已属于另一条逻辑链。对策是每次变更递增标签的版本化指针,以及推迟释放的 hazard pointer 与 epoch 回收。
使用场景
一切把 CAS 成功转成“内存结构未变”的结论的地方。托管运行时消灭了释放复用变体,但对象池、槽位与索引复用仍会造出逻辑 ABA。
常见误区
“GC 解决 ABA”只对了半个:GC 防释放后使用,防不了池化复用的逻辑 ABA。标签位有宽度上限,16 位标签在热结构里终会绕回零;hazard pointer 加的是每读者簿记成本,epoch 加的是回收延迟。优先用成熟库——手工修 ABA 的历史充满微妙失败。
代码示例
export type Release = () => void;
export class ReadWriteLock {
private readers = 0;
private writer = false;
private waitingReaders: Array<() => void> = [];
private waitingWriters: Array<() => void> = [];
async acquireRead(): Promise<Release> {
if (!this.writer && this.waitingWriters.length === 0) {
this.readers += 1;
return this.createReadRelease();
}
await new Promise<void>((resolve) => this.waitingReaders.push(resolve));
return this.createReadRelease();
}
async acquireWrite(): Promise<Release> {
if (!this.writer && this.readers === 0 && this.waitingWriters.length === 0) {
this.writer = true;
return this.createWriteRelease();
}
await new Promise<void>((resolve) => this.waitingWriters.push(resolve));
return this.createWriteRelease();
}
private createReadRelease(): Release {
let released = false;
return () => {
if (released) return;
released = true;
this.readers -= 1;
if (this.readers === 0 && this.waitingWriters.length > 0) {
this.writer = true;
this.waitingWriters.shift()!();
}
};
}
private createWriteRelease(): Release {
let released = false;
return () => {
if (released) return;
released = true;
this.writer = false;
if (this.waitingWriters.length > 0) {
this.writer = true;
this.waitingWriters.shift()!();
return;
}
const readers = this.waitingReaders.splice(0);
this.readers += readers.length;
for (const resolve of readers) resolve();
};
}
get state(): {
readers: number;
writer: boolean;
waitingReaders: number;
waitingWriters: number;
} {
return {
readers: this.readers,
writer: this.writer,
waitingReaders: this.waitingReaders.length,
waitingWriters: this.waitingWriters.length,
};
}
}
interface WeightedNode<T> {
value: T;
weight: number;
current: number;
}
export class SmoothWeightedRoundRobin<T> {
private readonly nodes: WeightedNode<T>[] = [];
private totalWeight = 0;
add(value: T, weight: number): this {
if (!Number.isInteger(weight) || weight < 1) {
throw new RangeError("weight must be a positive integer");
}
this.nodes.push({ value, weight, current: 0 });
this.totalWeight += weight;
return this;
}
remove(value: T): boolean {
const index = this.nodes.findIndex((node) => Object.is(node.value, value));
if (index === -1) return false;
this.totalWeight -= this.nodes[index].weight;
this.nodes.splice(index, 1);
return true;
}
next(): T {
if (this.nodes.length === 0) throw new Error("no nodes available");
let best = this.nodes[0];
for (const node of this.nodes) {
node.current += node.weight;
if (node.current > best.current) best = node;
}
best.current -= this.totalWeight;
return best.value;
}
get size(): number {
return this.nodes.length;
}
}