Atomics.add() คืออะไร
ฟังก์ชัน Atomics.add() ดำเนินการ บวก (addition) แบบอะตอมิก บนค่าที่อยู่ในตำแหน่งที่กำหนดของ typed array ที่แบ่งปัน ฟังก์ชันนี้รับ typed array, ตำแหน่ง (index), และค่า (value) แล้วบวกค่าที่กำหนดเข้ากับค่าปัจจุบัน เก็บผลลัพธ์ไว้ และคืนค่า เดิม (old value) ก่อนการบวก
นี่คือการดำเนินการ "fetch-and-add" แบบอะตอมิก ซึ่งเป็นพื้นฐานสำคัญของการเขียนโปรแกรมแบบ concurrent (concurrent programming) เช่น การนับตัวเลข (counters), การสร้าง ID แบบ unique, หรือการ implement locks
ไวยากรณ์ (Syntax)
Atomics.add(typedArray, index, value)
typedArray- Typed array ที่แบ่งปัน (Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, BigUint64Array)index- ตำแหน่งในอาร์เรย์value- ค่าที่จะบวก (สามารถเป็นค่าลบได้ เพื่อทำการลบ)
คืนค่า: ค่าที่อยู่ในตำแหน่ง index ก่อน การบวก
ตัวอย่างการใช้งาน
ตัวอย่างที่ 1: การบวกพื้นฐาน
const buffer = new SharedArrayBuffer(16);
const data = new Uint8Array(buffer);
data[0] = 3;
const oldValue = Atomics.add(data, 0, 3);
console.log('ค่าเดิม:', oldValue); // 3
console.log('ค่าใหม่:', Atomics.load(data, 0)); // 6
ตัวอย่างที่ 2: ตัวนับแบบ Thread-Safe (Atomic Counter)
class AtomicCounter {
constructor(initialValue = 0) {
this.buffer = new SharedArrayBuffer(8);
this.counter = new BigInt64Array(this.buffer);
this.counter[0] = BigInt(initialValue);
}
increment() {
// คืนค่าเดิม แล้วบวก 1
return Number(Atomics.add(this.counter, 0, 1n));
}
decrement() {
return Number(Atomics.add(this.counter, 0, -1n));
}
add(value) {
return Number(Atomics.add(this.counter, 0, BigInt(value)));
}
get() {
return Number(Atomics.load(this.counter, 0));
}
}
// การใช้งาน
const counter = new AtomicCounter(100);
console.log(counter.increment()); // 100 (ค่าเดิม), ตอนนี้คือ 101
console.log(counter.increment()); // 101, ตอนนี้คือ 102
console.log(counter.add(10)); // 102, ตอนนี้คือ 112
console.log(counter.get()); // 112
ตัวอย่างที่ 3: การสร้าง Unique ID แบบ Thread-Safe
const idBuffer = new SharedArrayBuffer(8);
const idGenerator = new BigInt64Array(idBuffer);
idGenerator[0] = 0n;
function generateUniqueId() {
// คืนค่า ID ปัจจุบัน แล้วเพิ่ม 1 สำหรับครั้งถัดไป
return Number(Atomics.add(idGenerator, 0, 1n));
}
// ใน Web Workers หลายตัว เรียก generateUniqueId() พร้อมกันได้อย่างปลอดภัย
// แต่ละครั้งจะได้ค่าที่ไม่ซ้ำกัน: 0, 1, 2, 3, ...
ตัวอย่างที่ 4: Spinlock แบบง่าย (Simple Spinlock)
class SpinLock {
constructor() {
this.buffer = new SharedArrayBuffer(4);
this.lock = new Int32Array(this.buffer);
this.lock[0] = 0; // 0 = unlocked, 1 = locked
}
acquire() {
// พยายามตั้งค่าเป็น 1 (locked) โดยใช้ compareExchange
while (true) {
const old = Atomics.compareExchange(this.lock, 0, 0, 1);
if (old === 0) break; // ได้ล็อกสำเร็จ
// ยังไม่ได้ล็อก รอสักครู่ (ในทางปฏิบัติควรใช้ Atomics.wait)
}
}
release() {
Atomics.store(this.lock, 0, 0); // ปลดล็อก
Atomics.notify(this.lock, 0, 1); // แจ้งเตือนเธรดที่รอ
}
}
คุณสมบัติสำคัญ
- Atomicity: การอ่าน-บวก-เขียน เกิดขึ้นเป็นหน่วยเดียว ไม่มี race condition
- คืนค่าเก่า: เหมาะสำหรับการ implement locks, semaphores, counters
- รองรับ BigInt: ตั้งแต่ ES2020 รองรับ
BigInt64ArrayและBigUint64Arrayสำหรับตัวเลขใหญ่ - Overflow: พฤติกรรมขึ้นอยู่กับ typed array (wrap-around สำหรับ unsigned, implementation-defined สำหรับ signed)
กรณีพิเศษและข้อผิดพลาด
- โยน
RangeErrorหากindexอยู่นอกขอบเขต - โยน
TypeErrorหากtypedArrayไม่ใช่ typed array ที่รองรับ - ไม่รองรับ
Float32Array,Float64Array