JavaScript - 原子操作物件



JavaScript 中的 Atomics 物件提供了一組靜態方法,用於對 SharedArrayBuffer 物件執行原子操作。原子操作是保證以單個步驟完成的操作,不會被其他執行緒中斷。這使得它們對於實現併發資料結構和演算法非常有用。

JavaScript 中的 Atomics 物件作為 ECMAScript 標準的一部分,是用於在多執行緒環境中管理共享記憶體的關鍵工具。讓我們更詳細地瞭解原子操作的基本概念。

原子操作物件

Atomics 物件是 JavaScript 內建物件,它提供對共享記憶體的原子操作。它旨在用於多執行緒環境,在該環境中,多個執行緒或 Web Workers 可能同時訪問和修改共享資料。

“原子”的本質

在 Atomics 物件的上下文中,“原子”表示一個關鍵特徵:它執行本質上不可分割的操作。當我們宣告一個操作為原子操作時,我們意味著它的執行是連續且不間斷地作為一個單元發生的。此特性對於防止競爭條件至關重要;當併發操作的結果取決於它們的執行時間和順序時,就會出現競爭條件。

原子操作

原子操作是對共享記憶體的低階操作,保證以單個、不可中斷的單元執行。這些操作包括加法、減法、位運算、交換等等。

Atomics 物件提供了 add、sub、and、or、xor、load、store、exchange 等方法,每個方法對應一個特定的原子操作。

序號 方法及描述
1

Atomics.add()

將指定值新增到型別化陣列中指定索引處的元素。以原子方式返回原始值。

2

Atomics.sub()

從型別化陣列中指定索引處的元素中減去指定值。以原子方式返回原始值。

3

Atomics.and()

對型別化陣列中指定索引處的元素與給定值執行原子按位 AND 操作。以原子方式返回原始值。

4

Atomics.or()

對型別化陣列中指定索引處的元素與給定值執行原子按位 OR 操作。以原子方式返回原始值。

5

Atomics.xor()

對型別化陣列中指定索引處的元素與給定值執行原子按位 XOR 操作。以原子方式返回原始值。

6

Atomics.load()

以原子方式檢索型別化陣列中指定索引處的值。

7

Atomics.store()

以原子方式將給定值儲存到型別化陣列中指定索引處。

8

Atomics.exchange()

將型別化陣列中指定索引處的值與指定值交換。以原子方式返回原始值。

9

Atomics. compareExchange()

將型別化陣列中指定索引處的值與提供的預期值進行比較,如果匹配,則使用新值更新該值。以原子方式返回原始值。

10

Atomics.wait()

以原子方式等待型別化陣列中指定索引處的值變為特定值,然後返回。允許執行緒之間有效協調。

11

Atomics.notify()

以原子方式通知與型別化陣列中指定索引關聯的等待佇列。

示例

示例 1:原子操作的基本用法

在此示例中,演示了 Atomics 物件對共享記憶體的基本原子操作。這些操作包括加法、減法、按位 AND、OR、XOR、載入、儲存、交換和比較交換值。每個操作都確保執行單元的不可分割性,這對於在多執行緒環境中防止競爭條件至關重要。

Atomics.add()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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()

// Shared memory setup
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() 操作遞增此計數器,從而保證更新過程的原子性。此類應用程式的功能和有效執行緒協調的必要性變得顯而易見:它在多執行緒環境中提供了實用的資料管理解決方案。

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

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

// Multiple threads incrementing the counter
setInterval(() => {
  incrementCounter();
}, 1000);

// Simulate other activities in the main thread
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.
...
廣告