Ang `SharedArrayBuffer` sa TypeScript

Ang `SharedArrayBuffer` sa TypeScript

Ang artikulong ito ay nagpapaliwanag ng SharedArrayBuffer sa TypeScript.

Ipapaliwanag namin ang SharedArrayBuffer sa TypeScript gamit ang mga praktikal na halimbawa.

YouTube Video

Ang SharedArrayBuffer sa TypeScript

Ang SharedArrayBuffer ay isang mekanismo para magbahagi ng parehong memory space sa pagitan ng maraming thread, gaya ng mga Web Worker. Sa pamamagitan ng pagsasama nito sa Atomics, maaari mong pamahalaan ang data races at magsagawa ng mabilis na shared memory operations na may mababang latency.

Mga Kailangang Kaalaman at Tala

Kapag ginagamit ang SharedArrayBuffer sa browser, kinakailangan ang COOP at COEP headers upang matugunan ang mga seguridad na kilala bilang cross-origin isolation. Sa Node.js, ang shared memory ay medyo madali lamang pamahalaan gamit ang worker_threads.

Pangunahing Konsepto

Ang SharedArrayBuffer ay isang object na kumakatawan sa isang fixed-length na sequence ng mga bytes, at maaari mong basahin at isulat ang mga numero sa pamamagitan ng TypedArray at katulad na mga view. Ang simpleng operations tulad ng pagbabasa o pagsusulat ay hindi synchronized, kaya ginagamit ang Atomics API upang matiyak ang atomic operations at gumagamit ng wait at notify na mekanismo para sa koordinasyon.

Simpleng Counter (Bersyon ng Browser)

Sa halimbawang ito, ang pangunahing thread ay lumilikha ng SharedArrayBuffer at ipinapasa ito sa isang Web Worker, at pareho nilang pinapataas ang halaga ng isang shared counter. Ipinapakita nito ang pinakapayak na pattern: atomic na pagdagdag gamit ang Atomics.add at pagbabasa gamit ang Atomics.load.

main.ts (Sa panig ng Browser)

Ipinapakita ng halimbawang ito kung paano lumilikha ng SharedArrayBuffer ang main thread at ibinabahagi ito sa isang Worker para sa sabay-sabay na pag-access.

 1// main.ts
 2// Create a 4-byte (Int32) shared buffer for one counter
 3const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 1);
 4const counter = new Int32Array(sab); // view over the buffer
 5
 6// Create worker and transfer the SharedArrayBuffer
 7const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' });
 8worker.postMessage({ sab });
 9
10// Increase counter in main thread every 200ms
11setInterval(() => {
12  const newVal = Atomics.add(counter, 0, 1) + 1; // Atomically increment
13  console.log('Main incremented ->', newVal);
14}, 200);
15
16// Listen for messages (optional)
17worker.onmessage = (e) => {
18  console.log('From worker:', e.data);
19};
  • Sa code na ito, ginagamit ng main thread ang Atomics.add upang madagdagan ang value nang atomic. Sa panig ng worker.ts, maaaring ma-access at mabago ang parehong SharedArrayBuffer.

worker.ts (Browser Worker)

Ito ay isang halimbawa kung saan ang worker ay tumatanggap ng parehong shared buffer at paminsan-minsan ay binabawasan o binabago ito.

 1// worker.ts
 2self.onmessage = (ev: MessageEvent) => {
 3  const { sab } = ev.data as { sab: SharedArrayBuffer };
 4  const counter = new Int32Array(sab);
 5
 6  // Every 350ms, atomically add 2 (demonstration)
 7  setInterval(() => {
 8    const newVal = Atomics.add(counter, 0, 2) + 2;
 9    // Optionally notify main thread (postMessage)
10    self.postMessage(`Worker added 2 -> ${newVal}`);
11  }, 350);
12};
  • Ang Worker ay nagmamaniobra rin ng parehong memory sa pamamagitan ng Int32Array, at ang mga pagbabago ay nagagawa nang walang race conditions dahil sa Atomics.

Pag-synchronize gamit ang wait/notify

Sa paggamit ng Atomics.wait at Atomics.notify, maaring ipahinto ang mga thread hanggang matupad ang ilang kondisyon, na nagpapahintulot ng event-driven na synchronization sa loob ng worker threads. Sa mga browser, ang pinakamaligtas na paraan ay gamitin ang Atomics.wait sa loob ng Worker.

producer.ts (Browser Producer Worker)

Ang producer ay sumusulat ng data at inaabisuhan ang consumer gamit ang notify.

 1// producer.ts (worker)
 2self.onmessage = (ev: MessageEvent) => {
 3  const { sab } = ev.data as { sab: SharedArrayBuffer };
 4  const state = new Int32Array(sab); // state[0] will be 0=empty,1=filled
 5
 6  // produce every 500ms
 7  setInterval(() => {
 8    // produce: set state to 1 (filled)
 9    Atomics.store(state, 0, 1);
10    Atomics.notify(state, 0, 1); // wake one waiter
11    self.postMessage('produced');
12  }, 500);
13};

consumer.ts (Browser Consumer Worker)

Ang consumer ay naghihintay gamit ang Atomics.wait at ipinagpapatuloy ang proseso kapag may abiso mula sa producer.

 1// consumer.ts (worker)
 2self.onmessage = (ev: MessageEvent) => {
 3  const { sab } = ev.data as { sab: SharedArrayBuffer };
 4  const state = new Int32Array(sab);
 5
 6  // consumer loop
 7  (async function consumeLoop() {
 8    while (true) {
 9      // if state is 0 => wait until notified (value changes)
10      while (Atomics.load(state, 0) === 0) {
11        // Blocks this worker until notified or timeout
12        Atomics.wait(state, 0, 0);
13      }
14      // consume (reset to 0)
15      // (processing simulated)
16      // read data from another shared buffer in real scenarios
17      Atomics.store(state, 0, 0);
18      // continue loop
19      self.postMessage('consumed');
20    }
21  })();
22};
  • Sa pattern na ito, ang producer ay nag-aabiso sa consumer sa pamamagitan ng Atomics.notify at ang consumer ay maayos na naghihintay gamit ang Atomics.wait. Ang Atomics.wait ay hindi maaaring tawagin sa main thread sa mga browser. Upang maiwasan ang pag-freeze ng UI, ang Atomics.wait ay limitado lamang sa paggamit sa loob ng Workers.

Praktikal na Halimbawa gamit ang Node.js (worker_threads)

Sa kapaligirang Node.js, maaari mong gamitin ang kakayahan ng SharedArrayBuffer gamit ang worker_threads. Narito ang isang halimbawa ng typed TypeScript para sa Node.js.

main-node.ts

Nililikha ng main thread ang buffer at ipinapasa ito sa Worker.

 1// main-node.ts
 2import { Worker } from 'worker_threads';
 3import path from 'path';
 4
 5// Create SharedArrayBuffer for one 32-bit integer
 6const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
 7const counter = new Int32Array(sab);
 8
 9// Spawn worker and pass sab via workerData
10const worker = new Worker(path.resolve(__dirname, 'worker-node.js'), {
11  workerData: { sab }
12});
13
14// Log messages from worker
15worker.on('message', (msg) => console.log('Worker:', msg));
16
17// Increment in main thread periodically
18setInterval(() => {
19  const val = Atomics.add(counter, 0, 1) + 1;
20  console.log('Main increment ->', val);
21}, 300);

worker-node.ts

Ang halimbawang ito ay gumagamit ng parentPort at workerData mula sa worker_threads sa panig ng Worker.

 1// worker-node.ts
 2import { parentPort, workerData } from 'worker_threads';
 3
 4const sab = workerData.sab as SharedArrayBuffer;
 5const counter = new Int32Array(sab);
 6
 7// Periodically add 5
 8setInterval(() => {
 9  const val = Atomics.add(counter, 0, 5) + 5;
10  parentPort?.postMessage(`Added 5 -> ${val}`);
11}, 700);
  • Sa Node.js, walang COOP o COEP na mga limitasyon katulad ng sa browser, kaya madali lang gamitin ang shared memory sa pamamagitan ng paggamit ng worker_threads. Kapag gumagamit ng TypeScript, siguraduhing tama ang settings sa pagitan ng CommonJS at ESM.

Mga Punto sa Tamang Pagkakatype ng TypeScript

Ang SharedArrayBuffer at Atomics ay kasama na sa standard na DOM at mga type definition ng library, kaya maaari mo itong direktang magamit sa TypeScript. Kapag nagpapalitan ng mensahe sa Workers, mas ligtas na magtakda ng interfaces at maging malinaw sa mga uri ng data (types).

1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };
  • Ang malinaw na pagde-define ng types ay nagpapaligtas sa paggamit ng postMessage at onmessage, at nagpapahintulot ng type checking.

Mga Praktikal na Paggamit

Hindi palaging kailangan ang SharedArrayBuffer, ngunit napakaepektibo ito sa mga sitwasyon na kinakailangan ang bilis na dulot ng shared memory. Ang pag-unawa sa mga sitwasyon kung saan ito epektibo ay nagreresulta sa tamang pagpili ng teknolohiya.

  • Angkop ito para sa mga proseso na nangangailangan ng mababang latency at mabilis na shared buffer, at maaaring magamit sa real-time na audio/video processing o game physics.
  • Para sa simpleng palitan ng data o pagtatransfer ng malalaking datos, maaaring mas madali ang paggamit ng Transferable ArrayBuffer o postMessage kaysa sa SharedArrayBuffer.

Mga Limitasyon at Seguridad

Para magamit ang SharedArrayBuffer sa browser, kinakailangan ang cross-origin isolation: itakda ang COOP sa same-origin-allow-popups at COEP sa require-corp. Hindi magagamit ang SharedArrayBuffer kung hindi natugunan ang mga rekisito na ito.

Mga Tip sa Performance at Debugging

Ang mga atomic operation (Atomics) ay mabilis, ngunit ang madalas na paghihintay at labis na synchronization ay maaaring magpataas ng latency.

Ang mga sumusunod na punto ay dapat suriin upang ligtas at mahusay na mapamahalaan ang shared memory.

  • Ang mga view tulad ng Int32Array ay dapat tama ang byte alignment.
  • Dapat malinaw kung aling indices ng parehong shared buffer ang ginagamit ng bawat proseso, at panatilihin ang pagkakapareho ng convention sa iyong code.
  • Kung tratuhin mo ang SharedArrayBuffer na parang normal na ArrayBuffer, lilitaw ang mga data race. Ang pagsusulat sa parehong index mula sa maraming thread nang hindi gumagamit ng Atomics ay mapanganib.

Buod

Sa pamamagitan ng tamang paggamit ng SharedArrayBuffer at Atomics, maaari kang magsagawa ng ligtas at mabilis na shared memory operations kahit sa TypeScript. Ang pagsisimula sa mga simpleng pattern ng synchronization tulad ng counters o signals ay mas madaling maintindihan, at kung maayos mong mapamahalaan ang tamang synchronization at mga index, magiging epektibo ka rin sa mga mababang-latency na sitwasyon.

Maaari mong sundan ang artikulo sa itaas gamit ang Visual Studio Code sa aming YouTube channel. Paki-check din ang aming YouTube channel.

YouTube Video