`SharedArrayBuffer` dalam TypeScript
Artikel ini menerangkan tentang SharedArrayBuffer dalam TypeScript.
Kami akan menerangkan SharedArrayBuffer dalam TypeScript dengan contoh-contoh praktikal.
YouTube Video
SharedArrayBuffer dalam TypeScript
SharedArrayBuffer ialah satu mekanisme untuk berkongsi ruang ingatan yang sama antara beberapa benang, seperti Web Workers. Dengan menggabungkannya bersama Atomics, anda boleh mengurus perlumbaan data (data races) dan melakukan operasi memori bersama dengan kependaman yang rendah.
Prasyarat dan Nota
Apabila menggunakan SharedArrayBuffer dalam pelayar, pengepala COOP dan COEP diperlukan untuk memenuhi keperluan keselamatan yang dikenali sebagai 'cross-origin isolation'. Dalam Node.js, memori bersama boleh dikendalikan dengan agak mudah menggunakan worker_threads.
Konsep Asas
SharedArrayBuffer ialah objek yang mewakili urutan bait sepanjang tetap, dan anda boleh membaca serta menulis nombor melalui TypedArray atau paparan serupa. Operasi baca/tulis yang mudah tidak diselaraskan, jadi anda perlu gunakan API Atomics untuk memastikan operasi atomik dan menggunakan mekanisme wait dan notify untuk penyelarasan.
Kaunter Mudah (Versi Pelayar)
Dalam contoh ini, benang utama mencipta SharedArrayBuffer dan menyerahkannya kepada Web Worker, dengan kedua-duanya menaikkan kaunter bersama. Ini menunjukkan corak paling asas: penambahan secara atomik dengan Atomics.add dan pembacaan dengan Atomics.load.
main.ts (Sisi Pelayar)
Contoh ini menunjukkan bagaimana benang utama mencipta SharedArrayBuffer dan berkongsikannya dengan Worker untuk capaian berbilang benang.
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 kod ini, benang utama menggunakan
Atomics.adduntuk menaikkan nilai secara atomik. Pada bahagianworker.ts,SharedArrayBufferyang sama boleh dicapai dan dimanipulasi.
worker.ts (Worker Pelayar)
Ini adalah contoh di mana worker menerima buffer bersama yang sama dan secara berkala mengurangkan atau memanipulasikannya.
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 kemas kini dilakukan tanpa keadaan berlumba (race conditions) disebabkan penggunaanAtomics.
Penyelarasan menggunakan wait/notify
Dengan menggunakan Atomics.wait dan Atomics.notify, anda boleh menggantung benang sehingga syarat tertentu dipenuhi, membolehkan penyelarasan berasaskan peristiwa dalam benang worker. Dalam pelayar, kaedah paling selamat adalah menggunakan Atomics.wait di dalam Worker.
producer.ts (Worker Pengeluar Pelayar)
Pengeluar menulis data dan memberitahu pengguna (consumer) 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 Pengguna Pelayar)
Pengguna (consumer) menunggu dengan Atomics.wait dan menyambung semula pemprosesan apabila menerima pemberitahuan daripada pengeluar.
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 corak ini, pengeluar memberitahu pengguna melalui
Atomics.notifydan pengguna menunggu secara efisien menggunakanAtomics.wait.Atomics.waittidak boleh dipanggil pada benang utama dalam pelayar. Bagi mengelakkan antara muka pengguna (UI) daripada membeku,Atomics.waitdihadkan untuk digunakan hanya dalam Worker sahaja.
Contoh Praktikal dengan Node.js (worker_threads)
Dalam persekitaran Node.js, anda boleh mengendalikan fungsi SharedArrayBuffer menggunakan worker_threads. Di bawah ini adalah contoh TypeScript dengan ‘typing’ untuk Node.js.
main-node.ts
Benang utama mencipta buffer dan menyerahkannya kepada 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 daripada worker_threads di bahagian 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);- Dalam Node.js, tiada sekatan COOP atau COEP seperti dalam pelayar, jadi memori bersama boleh dikendalikan dengan mudah hanya menggunakan
worker_threads. Apabila menggunakan TypeScript, beri perhatian sama ada anda membina dengan tetapan CommonJS atau ESM.
Titik-titik Pengetikan TypeScript
SharedArrayBuffer dan Atomics sudah termasuk dalam definisi jenis (type definition) piawai DOM dan pustaka, jadi anda boleh menggunakannya secara langsung dalam TypeScript. Apabila bertukar mesej dengan Worker, adalah lebih selamat untuk menentukan antara muka (interface) dan jenis (type) dengan jelas.
1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };- Menetapkan jenis (type) secara jelas menjadikan pengendalian
postMessagedanonmessagelebih selamat serta membolehkan semakan jenis (type checking).
Kes Penggunaan Praktikal
SharedArrayBuffer tidak sentiasa diperlukan, tetapi ia sangat berkesan dalam situasi di mana kelebihan kelajuan memori bersama diperlukan. Memahami situasi di mana ia berkesan akan membawa kepada pilihan teknologi yang sesuai.
- Ia sesuai untuk pemprosesan kependaman rendah yang memerlukan buffer bersama berkelajuan tinggi dan boleh digunakan untuk pemprosesan audio/video masa nyata atau fizik dalam permainan.
- Untuk pertukaran data yang ringkas atau pemindahan data yang besar,
Transferable ArrayBufferataupostMessagemungkin lebih mudah digunakan berbandingSharedArrayBuffer.
Kekangan dan Keselamatan
Untuk menggunakan SharedArrayBuffer dalam pelayar, pengasingan rentas asal diperlukan: tetapkan COOP kepada same-origin-allow-popups dan COEP kepada require-corp. SharedArrayBuffer akan dilumpuhkan jika keperluan ini tidak dipenuhi.
Petua Prestasi dan Penyahpepijatan
Operasi atomik (Atomics) adalah pantas, tetapi menunggu terlalu kerap dan penyelarasan berlebihan boleh meningkatkan kependaman.
Perkara berikut harus diperiksa untuk mengendalikan memori bersama dengan selamat dan cekap.
- Paparan seperti
Int32Arrayperlu dikendalikan dengan penjajaran byte yang betul. - Jelaskan indeks mana dalam buffer bersama yang digunakan oleh proses mana, dan kekalkan konvensyen yang konsisten dalam kod anda.
- Jika anda melayan
SharedArrayBuffersepertiArrayBufferbiasa, perlumbaan data (data races) akan berlaku. Menulis ke indeks yang sama dari berbilang benang tanpa menggunakanAtomicsadalah berbahaya.
Ringkasan
Dengan menggunakan SharedArrayBuffer dan Atomics dengan betul, anda boleh mencapai operasi memori bersama yang selamat dan pantas walaupun dalam TypeScript. Bermula dengan corak penyelarasan mudah seperti kaunter atau isyarat lebih mudah difahami, dan dengan mengurus penyelarasan serta indeks dengan teliti, anda boleh berkesan walaupun dalam senario kependaman rendah.
Anda boleh mengikuti artikel di atas menggunakan Visual Studio Code di saluran YouTube kami. Sila lihat juga saluran YouTube kami.