Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

ฟังก์ชัน Atomics.add() ใน JavaScript

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