JavaScript - Atomics 对象 (原子对象)



JavaScript 中的 Atomics 对象提供了一组静态方法,用于对 SharedArrayBuffer 对象执行原子操作。原子操作是保证在单个步骤中完成的操作,不会被其他线程中断。这使得它们可用于实现并发数据结构和算法。

JavaScript 中的 Atomics 对象作为 ECMAScript 标准的一部分,是在多线程环境中管理共享内存的重要工具。让我们更详细地了解原子操作的基本概念:

Atomics 对象

Atomics 对象是一个内置的 JavaScript 对象,它提供对共享内存的原子操作。它旨在用于多线程环境,其中多个线程或 Web Worker 可能同时访问和修改共享数据。

“Atomic” 的精髓

在 Atomics 对象的上下文中,“原子”表示一个关键特征:它执行本质上不可分割的操作。当我们声明一个操作为 atomic 时;我们暗示它的执行会像单个单元一样连续且不间断地进行。这种品质对于防止竞争条件是必不可少的;当并发操作的结果取决于其执行时间和顺序时,就会出现这些变化。

Atomics 操作

Atomics 操作是对共享内存的低级操作,保证作为单个不可中断的单元执行。这些操作包括加法、减法、按位运算、交换等。

Atomics 对象提供 add、sub、and、or、xor、load、store、exchange 等方法,每个方法都对应于特定的原子操作。

方法 描述
Atomics.add()

将指定值添加到类型化数组中指定索引处的元素。以 Atomics 方式返回原始值。

Atomics.sub()

从类型化数组中指定索引处的元素中减去指定值。以 Atomics 方式返回原始值。

Atomics.and()

对具有给定值的类型化数组中指定索引处的元素执行 Atomics 按位 AND 运算。以 Atomics 方式返回原始值。

Atomics.or()

对具有给定值的类型化数组中指定索引处的元素执行 Atomics 按位 OR 运算。以 Atomics 方式返回原始值。

Atomics.xor()

对具有给定值的类型化数组中指定索引处的元素执行 Atomics 按位 XOR 运算。以 Atomics 方式返回原始值。

Atomics.load()

以 Atomics 方式检索类型化数组中指定索引处的值。

Atomics.store()

以 Atomics 方式将给定值存储在类型化数组中的指定索引处。

Atomics.exchange()

将类型化数组中指定索引处的值与指定值交换。以 Atomics 方式返回原始值。

Atomics. compareExchange()

将类型化数组中指定索引处的值与提供的预期值进行比较,如果它们匹配,则使用新值更新该值。以 Atomics 方式返回原始值。

Atomics.wait()

以 Atomics 方式等待类型化数组中指定索引处的值为特定值,然后返回。允许在线程之间进行有效协调。

Atomics.notify()

以 Atomics 方式通知与类型化数组中的指定索引关联的等待队列。

例子

示例 1: Atomics 操作的基本用法

在此示例中,演示了 Atomics 对象对共享内存的基本原子操作。这些操作包括加法、减法、按位 AND、OR、XOR、加载、存储、交换和比较交换值。每个操作都确保了执行单元的不可分割性,这对于防止多线程环境中的争用条件至关重要。

Atomics.add()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.add()
const originalAddValue = Atomics.add(sharedArray, 0, 10);
console.log(`Atomics.add: Original value: ${originalAddValue}, New value: ${sharedArray[0]}`);

输出

Atomics.add: Original value: 0, New value: 10

Atomics.add()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.sub()
const originalSubValue = Atomics.sub(sharedArray, 0, 5);
console.log(`Atomics.sub: Original value: ${originalSubValue}, New value: ${sharedArray[0]}`);

输出

Atomics.sub: Original value: 10, New value: 5

Atomics.add()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.and()
const originalAndValue = Atomics.and(sharedArray, 0, 0b1010);
console.log(`Atomics.and: Original value: ${originalAndValue}, New value: ${sharedArray[0].toString(2)}`);

输出

Atomics.and: Original value: 5, New value: 0

Atomics.or()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.or()
const originalOrValue = Atomics.or(sharedArray, 0, 0b1100);
console.log(`Atomics.or: Original value: ${originalOrValue}, New value: ${sharedArray[0].toString(2)}`);

输出

Atomics.or: Original value: 0, New value: 1100

Atomics.xor()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.xor()
const originalXorValue = Atomics.xor(sharedArray, 0, 0b0110);
console.log(`Atomics.xor: Original value: ${originalXorValue}, New value: ${sharedArray[0].toString(2)}`);

输出

Atomics.xor: Original value: 12, New value: 1010

Atomics.load()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.load()
const loadedValue = Atomics.load(sharedArray, 0);
console.log(`Atomics.load: Loaded value: ${loadedValue}`);

输出

Atomics.load: Loaded value: 10

Atomics.store()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.store()
Atomics.store(sharedArray, 0, 42);
console.log(`Atomics.store: New value: ${sharedArray[0]}`);

输出

Atomics.store: New value: 42

Atomics.exchange()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.exchange()
const originalExchangeValue = Atomics.exchange(sharedArray, 0, 99);
console.log(`Atomics.exchange: Original value: ${originalExchangeValue}, New value: ${sharedArray[0]}`);

输出

Atomics.exchange: Original value: 42, New value: 99

Atomics.compareExchange()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.compareExchange()
const expectedValue = 99;
const newValue = 55;
const successfulCompareExchange = Atomics.compareExchange(sharedArray, 0, expectedValue, newValue);
console.log(`Atomics.compareExchange: Operation was${successfulCompareExchange ? ' ' : ' not '}successful. New value: ${sharedArray[0]}`);

输出

Atomics.compareExchange: Operation was successful. New value: 55

Atomics.wait()


// 共享内存设置
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Atomics.wait()
const valueToWaitFor = 55;
Atomics.store(sharedArray, 0, valueToWaitFor);
setTimeout(() => {
	 	 Atomics.notify(sharedArray, 0);
}, 2000);
const waitResult = Atomics.wait(sharedArray, 0, valueToWaitFor, 5000);
console.log(`Atomics.wait: Wait result: ${waitResult}`);

输出

Atomics.wait: Wait result: timed-out

示例 2:实际用例 - 同步计数器

在这个实际场景中,我们使用 Atomics 对象来构造一个同步计数器;多个线程通过使用 Atomics.add() 操作来增加此计数器,从而保证更新过程中的 Atomics 性。在这样的应用程序中,有效线程协调的功能和必要性变得显而易见:它在多线程环境中提供实用的数据管理解决方案。


const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);

// 同步计数器
function incrementCounter() {
	 const incrementValue = 1;
	 const originalValue = Atomics.add(sharedArray, 0, incrementValue);
	 console.log(`Incremented counter by ${incrementValue}. New value: ${sharedArray[0]}`);
}

// 多个线程递增计数器
setInterval(() => {
	 incrementCounter();
}, 1000);

// 模拟主线程中的其他活动
setInterval(() => {
	 console.log('Main thread doing other work.');
}, 3000);

输出

Incremented counter by 1. New value: 1
Incremented counter by 1. New value: 2
Main thread doing other work.
Incremented counter by 1. New value: 3
Incremented counter by 1. New value: 4
Incremented counter by 1. New value: 5
Main thread doing other work.
Incremented counter by 1. New value: 6
Incremented counter by 1. New value: 7
Incremented counter by 1. New value: 8
Main thread doing other work.
...