`SharedArrayBuffer` dalam TypeScript
Artikel ini menjelaskan tentang SharedArrayBuffer dalam TypeScript.
Kami akan menjelaskan SharedArrayBuffer di TypeScript dengan contoh praktis.
YouTube Video
SharedArrayBuffer dalam TypeScript
SharedArrayBuffer adalah mekanisme untuk berbagi ruang memori yang sama antara beberapa thread, seperti Web Workers. Dengan menggabungkannya dengan Atomics, Anda dapat mengelola data race dan melakukan operasi memori bersama dengan latensi rendah.
Prasyarat dan Catatan
Saat menggunakan SharedArrayBuffer di browser, header COOP dan COEP diperlukan untuk memenuhi persyaratan keamanan yang dikenal sebagai isolasi lintas asal (cross-origin isolation). Di Node.js, memori bersama dapat dikelola dengan relatif mudah menggunakan worker_threads.
Konsep Dasar
SharedArrayBuffer adalah objek yang merepresentasikan urutan byte dengan panjang tetap, dan Anda dapat membaca serta menulis angka melalui TypedArray dan tampilan serupa. Operasi baca/tulis sederhana tidak tersinkronisasi, jadi Anda menggunakan API Atomics untuk memastikan operasi atomik dan menggunakan mekanisme wait dan notify untuk koordinasi.
Penghitung Sederhana (versi Browser)
Dalam contoh ini, thread utama membuat SharedArrayBuffer dan memberikannya ke Web Worker, dimana keduanya menaikkan penghitung bersama. Ini menunjukkan pola minimal: penjumlahan atomik dengan Atomics.add dan pembacaan dengan Atomics.load.
main.ts (Sisi Browser)
Contoh ini menunjukkan bagaimana thread utama membuat SharedArrayBuffer dan membagikannya dengan Worker untuk akses multi-thread.
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};- Dalam kode ini, thread utama menggunakan
Atomics.adduntuk menambah nilai secara atomik. Di sisiworker.ts,SharedArrayBufferyang sama dapat diakses dan dimanipulasi.
worker.ts (Browser Worker)
Ini adalah contoh di mana worker menerima buffer bersama yang sama dan secara berkala mengurangi atau memanipulasinya.
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};- Worker juga memanipulasi memori yang sama melalui
Int32Array, dan pembaruan dilakukan tanpa kondisi balapan berkatAtomics.
Sinkronisasi menggunakan wait/notify
Dengan menggunakan Atomics.wait dan Atomics.notify, Anda dapat menangguhkan thread hingga kondisi tertentu terpenuhi, memungkinkan sinkronisasi berbasis peristiwa di dalam thread worker. Di browser, pendekatan paling aman adalah menggunakan Atomics.wait di dalam Worker.
producer.ts (Worker Produsen Browser)
Produsen menulis data dan memberi tahu konsumen menggunakan 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 (Worker Konsumen Browser)
Konsumen menunggu dengan Atomics.wait dan melanjutkan pemrosesan setelah menerima pemberitahuan dari produsen.
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};- Dalam pola ini, produsen memberi tahu konsumen melalui
Atomics.notifydan konsumen menunggu secara efisien menggunakanAtomics.wait.Atomics.waittidak dapat dipanggil pada thread utama di browser. Untuk mencegah UI membeku,Atomics.waithanya boleh digunakan di dalam Worker.
Contoh Praktis dengan Node.js (worker_threads)
Di lingkungan Node.js, Anda dapat menangani fungsi SharedArrayBuffer dengan menggunakan worker_threads. Di bawah ini adalah contoh TypeScript dengan tipe untuk Node.js.
main-node.ts
Thread utama membuat buffer dan memberikannya ke 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
Contoh ini menggunakan parentPort dan workerData dari worker_threads di sisi 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);- Di Node.js, tidak ada pembatasan COOP atau COEP seperti di browser, sehingga memori bersama dapat dikelola dengan mudah hanya dengan menggunakan
worker_threads. Saat menggunakan TypeScript, perhatikan apakah Anda membangun dengan pengaturan CommonJS atau ESM.
Poin Pengetikan TypeScript
SharedArrayBuffer dan Atomics sudah disertakan dalam definisi tipe standar DOM dan library, sehingga Anda bisa langsung menggunakannya di TypeScript. Saat bertukar pesan dengan Worker, lebih aman untuk mendefinisikan antarmuka dan menentukan tipe secara jelas.
1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };- Mendefinisikan tipe secara eksplisit membuat penanganan
postMessagedanonmessagemenjadi lebih aman dan memungkinkan pemeriksaan tipe.
Kasus Penggunaan Praktis
SharedArrayBuffer tidak selalu diperlukan, tetapi sangat efektif dalam situasi di mana kecepatan memori bersama dibutuhkan. Memahami situasi di mana mekanisme ini efektif akan membantu dalam memilih teknologi yang tepat.
- Ini cocok untuk pemrosesan latensi rendah yang membutuhkan buffer bersama berkecepatan tinggi dan dapat digunakan untuk pemrosesan audio/video real-time atau fisika game.
- Untuk pertukaran data sederhana atau transfer data dalam jumlah besar,
Transferable ArrayBufferataupostMessagemungkin lebih mudah digunakan dibandingkanSharedArrayBuffer.
Batasan dan Keamanan
Untuk menggunakan SharedArrayBuffer di browser, diperlukan isolasi lintas asal: atur COOP ke same-origin-allow-popups dan COEP ke require-corp. SharedArrayBuffer akan dinonaktifkan jika persyaratan ini tidak terpenuhi.
Tips Performa dan Debugging
Operasi atomik (Atomics) itu cepat, tetapi penantian yang sering dan sinkronisasi yang berlebihan dapat meningkatkan latensi.
Poin-poin berikut harus diperiksa untuk menangani memori bersama dengan aman dan efisien.
- Tampilan seperti
Int32Arrayharus ditangani dengan penyelarasan byte yang tepat. - Jelaskan dengan jelas indeks mana pada buffer bersama yang digunakan oleh proses tertentu, dan pertahankan konvensi yang konsisten dalam kode Anda.
- Jika Anda memperlakukan
SharedArrayBuffersepertiArrayBufferbiasa, akan terjadi data race. Menulis ke indeks yang sama dari beberapa thread tanpa menggunakanAtomicssangat berbahaya.
Ringkasan
Dengan memanfaatkan SharedArrayBuffer dan Atomics dengan benar, Anda dapat mencapai operasi memori bersama yang aman dan cepat bahkan di TypeScript. Memulai dengan pola sinkronisasi sederhana seperti penghitung atau sinyal lebih mudah dipahami, dan dengan mengelola sinkronisasi serta indeks secara benar, Anda bisa efektif bahkan dalam skenario latensi rendah.
Anda dapat mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Silakan periksa juga saluran YouTube kami.