Berkongsi Memori dengan SharedArrayBuffer dan Atomics
Selaraskan utas melalui memori dikongsi menggunakan Atomics untuk mengelakkan penyalinan penimbal besar dan keadaan perlumbaan.
Berkongsi Memori dengan SharedArrayBuffer dan Atomics ialah pelajaran Kem Intensif Pembangunan Backend Node.js percuma di CoddyKit. Ini ialah pelajaran 3 daripada 4. Anda boleh membaca keseluruhan pelajaran di bawah secara percuma — kemudian berlatih secara praktikal dalam pelayar menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7. Pelajaran ini merupakan sebahagian daripada laluan pembelajaran Kem Intensif Pembangunan Backend Node.js, dan kemajuan anda disegerakkan merentas web serta aplikasi CoddyKit. Kursus Kem Intensif Pembangunan Backend Node.js merangkumi sejumlah 4 pelajaran.
Mengapa Memori Dikongsi?
Apabila anda mencipta Worker dalam Node.js dan memanggil postMessage, data yang dihantar akan disalin menggunakan algoritma klon berstruktur. Untuk mesej kecil, perkara ini tidak menjadi masalah, tetapi bagi penimbal angka yang besar (jutaan bait), penyalinan membazirkan CPU dan memori.
- SharedArrayBuffer membolehkan beberapa utas membaca dan menulis pada blok memori yang sama tanpa penyalinan.
- Atomics menyediakan operasi yang selamat dan bebas keadaan perlumbaan pada memori tersebut.
Pelajaran ini menunjukkan cara menyelaraskan utas melalui memori dikongsi dalam tugas bahagian belakang yang terikat CPU.
ArrayBuffer berbanding SharedArrayBuffer
ArrayBuffer dimiliki oleh satu utas. Apabila dipindahkan kepada pekerja, pengirim tidak lagi dapat mengaksesnya. SharedArrayBuffer (SAB) pula berbeza: apabila dihantar kepada pekerja, ia berkongsi stor sokongan yang sama, jadi kedua-dua utas dapat melihat penulisan satu sama lain.
Anda tidak membaca bait mentah secara langsung. Sebaliknya, anda membalut penimbal itu dalam paparan tatasusunan bertip seperti Int32Array atau Float64Array.
const sab = new SharedArrayBuffer(16);
const view = new Int32Array(sab);
console.log(view.length);
view[0] = 42;
console.log(view[0]);
console.log(sab.byteLength);Menghantar SAB kepada pekerja
Untuk berkongsi memori, cipta SharedArrayBuffer dalam utas utama dan hantarkannya melalui postMessage. Tidak seperti penimbal biasa, SAB dikongsi (bukan dipindahkan), jadi kedua-dua pihak terus menggunakannya.
Pekerja membalut SAB yang sama dalam paparan tatasusunan bertipnya sendiri. Tiada penyalinan berlaku.
const { Worker, isMainThread, workerData } = require('worker_threads');
if (isMainThread) {
const sab = new SharedArrayBuffer(4);
const view = new Int32Array(sab);
view[0] = 100;
new Worker(__filename, { workerData: sab });
} else {
const view = new Int32Array(workerData);
view[0] += 1;
console.log('worker sees', view[0]);
}Masalah Keadaan Perlumbaan
Pembacaan dan penulisan biasa pada paparan dikongsi tidak selamat apabila beberapa utas mengakses slot yang sama. Pernyataan seperti view[0] += 1 sebenarnya terdiri daripada tiga langkah: baca, tambah, tulis. Dua utas boleh berselang-seli lalu menyebabkan kemas kini hilang.
- Utas A membaca 5, utas B membaca 5.
- Kedua-duanya mengira 6 dan menulis 6.
- Dua penambahan berlaku, tetapi nilai hanya meningkat sebanyak satu.
Ini ialah perlumbaan data klasik. Penyelesaiannya ialah Atomics.
Atomics.add untuk Pembilang yang Selamat
Atomics.add(view, index, value) melaksanakan baca-ubah-suai-tulis sebagai satu operasi yang tidak boleh dipecahkan. Tiada utas lain boleh menyelit di antaranya, jadi penambahan tidak akan hilang.
Kaedah lain yang berguna: Atomics.sub, Atomics.and, Atomics.or, serta Atomics.load / Atomics.store untuk pembacaan dan penulisan biasa yang dijamin dapat dilihat merentas utas.
const sab = new SharedArrayBuffer(4);
const counter = new Int32Array(sab);
Atomics.store(counter, 0, 0);
Atomics.add(counter, 0, 5);
Atomics.add(counter, 0, 3);
console.log(Atomics.load(counter, 0));Membahagikan Kerja CPU Merentas Utas
Bayangkan anda menjumlahkan tatasusunan integer yang sangat besar, iaitu tugas terikat CPU yang akan menyekat gelung peristiwa. Dengan memori dikongsi, anda menyimpan data sekali sahaja dan membenarkan beberapa pekerja memproses setiap bahagian, lalu menulis hasil separa ke dalam slot output dikongsi melalui Atomics.add.
Oleh sebab input berada dalam SharedArrayBuffer, anda tidak perlu menyalin set data kepada setiap pekerja. Kesemuanya membaca bait yang sama.
Banding dan Tukar
Atomics.compareExchange(view, index, expected, replacement) menulis replacement hanya jika nilai semasa sama dengan expected, dan mengembalikan nilai yang berada di situ. Ini ialah asas bagi algoritma tanpa kunci dan kunci putaran ringkas.
Gunakannya untuk menuntut slot tepat sekali: jika pertukaran berjaya, utas ini memenangi tuntutan tersebut.
const sab = new SharedArrayBuffer(4);
const slot = new Int32Array(sab);
Atomics.store(slot, 0, 0);
const prev = Atomics.compareExchange(slot, 0, 0, 1);
console.log('previous value was', prev);
console.log('claimed:', prev === 0);
const again = Atomics.compareExchange(slot, 0, 0, 1);
console.log('second claim succeeded:', again === 0);Menyekat dengan Atomics.wait
Adakalanya pekerja perlu berhenti seketika sehingga utas lain memberinya isyarat. Atomics.wait(view, index, expectedValue) menyekat utas pemanggil selagi slot masih mengandungi expectedValue. Ia mengembalikan 'ok', 'not-equal', atau 'timed-out'.
Atomics.waithanya berfungsi di luar utas utama (jika tidak, gelung peristiwa akan terhenti).Atomics.notify(view, index, count)membangunkan utas yang sedang menunggu.
Ini memberikan anda penghalang utas sebenar tanpa gelung sibuk.
Notify untuk Membangunkan Pekerja
Utas pengeluar mengemas kini slot dikongsi dengan Atomics.store, kemudian memanggil Atomics.notify untuk membangunkan mana-mana utas yang sedang menunggu dalam Atomics.wait. Susunannya penting: ubah nilai dahulu, kemudian beri isyarat.
Dalam tugas bahagian belakang sebenar, cara ini digunakan oleh penyelaras untuk melepaskan semua pekerja serentak bagi memulakan satu fasa, atau untuk menandakan bahawa input sudah tersedia.
const { Worker, isMainThread, workerData } = require('worker_threads');
if (isMainThread) {
const sab = new SharedArrayBuffer(4);
const signal = new Int32Array(sab);
Atomics.store(signal, 0, 0);
new Worker(__filename, { workerData: sab });
setTimeout(() => {
Atomics.store(signal, 0, 1);
Atomics.notify(signal, 0, 1);
}, 50);
} else {
const signal = new Int32Array(workerData);
Atomics.wait(signal, 0, 0);
console.log('worker released, value =', Atomics.load(signal, 0));
}Jumlah Selari Lengkap
Berikut ialah corak lengkap dalam satu fail yang boleh dijalankan: penimbal input dikongsi, slot hasil dikongsi, dan dua pekerja yang masing-masing menjumlahkan separuh data lalu menambah hasil separa mereka secara atomik ke dalam hasil tersebut. Utas utama menunggu sehingga kedua-duanya selesai.
Perhatikan bahawa input tidak pernah disalin; kedua-dua pekerja membaca SharedArrayBuffer yang sama.
const { Worker, isMainThread, workerData } = require('worker_threads');
if (isMainThread) {
const N = 1000;
const dataSab = new SharedArrayBuffer(N * 4);
const data = new Int32Array(dataSab);
for (let i = 0; i < N; i++) data[i] = i + 1;
const resultSab = new SharedArrayBuffer(8);
const result = new Int32Array(resultSab);
Atomics.store(result, 0, 0);
Atomics.store(result, 1, 0);
let done = 0;
const ranges = [[0, N / 2], [N / 2, N]];
for (const [start, end] of ranges) {
const w = new Worker(__filename, { workerData: { dataSab, resultSab, start, end } });
w.on('exit', () => {
if (++done === ranges.length) {
console.log('total =', Atomics.load(result, 0));
}
});
}
} else {
const { dataSab, resultSab, start, end } = workerData;
const data = new Int32Array(dataSab);
const result = new Int32Array(resultSab);
let local = 0;
for (let i = start; i < end; i++) local += data[i];
Atomics.add(result, 0, local);
}Peringatan Praktikal
Memori dikongsi sangat berkuasa tetapi mudah disalahgunakan. Ingat peraturan berikut:
SharedArrayBufferhanya menyimpan nombor. Untuk berkongsi rentetan atau objek, anda mesti mengekodkannya (contohnya denganTextEncoderke dalamUint8Array).- Sentiasa gunakan
Atomicsuntuk mana-mana slot yang mungkin ditulis oleh lebih daripada satu utas; mencampurkan penulisan biasa dengan penulisan atomik akan memperkenalkan semula keadaan perlumbaan. - Sediakan slot tetap untuk bendera penyegerakan dan dokumentasikan indeksnya.
- Gunakan memori dikongsi hanya apabila penyalinan benar-benar menjadi kesesakan; bagi kebanyakan mesej,
postMessagebiasa lebih mudah dan selamat.
Semakan Pantas
Anda mempunyai beberapa utas pekerja yang menambah satu pembilang dikongsi yang disimpan dalam Int32Array dan disokong oleh SharedArrayBuffer. Pendekatan manakah yang memastikan kiraan kekal betul semasa keserentakan?
Imbas Kembali
Anda telah mempelajari cara menyelaraskan utas pekerja Node.js melalui memori dikongsi:
- SharedArrayBuffer berkongsi satu stor sokongan merentas utas tanpa penyalinan; balutkannya dalam tatasusunan bertip seperti
Int32Array. +=biasa pada slot dikongsi menyebabkan perlumbaan data; gunakan Atomics untuk mana-mana slot yang ditulis secara serentak.Atomics.add,Atomics.load,Atomics.store, danAtomics.compareExchangemenyediakan pembacaan, penulisan, dan tuntutan tanpa keadaan perlumbaan.Atomics.wait(di luar utas utama) bersama-samaAtomics.notifymembolehkan utas disekat dan diberi isyarat tanpa gelung sibuk.- Gunakan memori dikongsi hanya apabila penyalinan penimbal angka yang besar benar-benar menjadi kesesakan.
Pelajari JavaScript dengan tutor kecerdasan buatan — percuma
Tulis dan jalankan kod sebenar dalam pelayar anda, dapatkan bantuan segera daripada tutor kecerdasan buatan yang tersedia 24/7, dan sambung semula dari tempat anda berhenti di web atau dalam aplikasi.
- Kursus
- 22
- Pelajaran
- 92
Soalan Lazim
Adakah pelajaran “Berkongsi Memori dengan SharedArrayBuffer dan Atomics” percuma?
Ya — teks penuh “Berkongsi Memori dengan SharedArrayBuffer dan Atomics” boleh dibaca secara percuma di web ini. Untuk berlatih secara interaktif menggunakan penyunting kod terbina dalam dan tutor kecerdasan buatan 24/7, serta membuka kunci baki kursus Kem Intensif Pembangunan Backend Node.js, tingkat taraf kepada CoddyKit PRO. Kursus Kem Intensif Pembangunan Backend Node.js merangkumi sejumlah 4 pelajaran.
Apakah yang akan saya pelajari dalam “Berkongsi Memori dengan SharedArrayBuffer dan Atomics”?
Selaraskan utas melalui memori dikongsi menggunakan Atomics untuk mengelakkan penyalinan penimbal besar dan keadaan perlumbaan. Anda berlatih Kem Intensif Pembangunan Backend Node.js menggunakan kod praktikal yang dijalankan terus dalam pelayar, manakala tutor kecerdasan buatan 24/7 menjawab soalan anda semasa anda mengikuti pelajaran.
Adakah saya memerlukan pengalaman untuk memulakan Kem Intensif Pembangunan Backend Node.js?
Tiada pengalaman terdahulu diperlukan. Pembelajaran Kem Intensif Pembangunan Backend Node.js di CoddyKit disusun untuk pelajar daripada peringkat pemula hingga lanjutan, jadi anda boleh bermula di sini atau dari awal dan belajar mengikut kadar anda sendiri. Ini ialah pelajaran 3 daripada 4.
Berapa lamakah pelajaran “Berkongsi Memori dengan SharedArrayBuffer dan Atomics” diambil?
Kebanyakan pelajaran CoddyKit mengambil masa kira-kira 5–10 minit. Setiap pelajaran ringkas dan interaktif, jadi anda boleh membuat kemajuan secara berterusan dan menyambung tepat dari tempat anda berhenti di web atau aplikasi.
Bolehkah saya menulis dan menjalankan kod dalam pelajaran Kem Intensif Pembangunan Backend Node.js ini?
Ya. Setiap pelajaran Kem Intensif Pembangunan Backend Node.js menyertakan penyunting kod terbina dalam, jadi anda boleh menulis dan menjalankan kod sebenar terus dalam pelayar serta menerima maklum balas kecerdasan buatan serta-merta — tanpa memerlukan persediaan setempat.
Semua pelajaran dalam kursus ini
- Mengapa Gelung Peristiwa Terhenti pada Kerja Terikat CPU
- Mencipta Utas Pekerja dan Menghantar Mesej
- Berkongsi Memori dengan SharedArrayBuffer dan Atomics
- Membina Kumpulan Pekerja Boleh Guna Semula untuk Daya Pemprosesan