TypeScript में `SharedArrayBuffer`
यह लेख TypeScript में SharedArrayBuffer को समझाता है।
हम व्यावहारिक उदाहरणों के साथ TypeScript में SharedArrayBuffer को समझाएंगे।
YouTube Video
TypeScript में SharedArrayBuffer
SharedArrayBuffer एक तंत्र है जो कई थ्रेड्स, जैसे कि Web Workers, के बीच एक ही मेमोरी स्पेस साझा करने की अनुमति देता है। Atomics के साथ इसे मिलाकर, आप डेटा रेस को नियंत्रित कर सकते हैं और कम विलंबता वाली साझा मेमोरी ऑपरेशन कर सकते हैं।
पूर्वापेक्षाएँ और नोट्स
ब्राउज़र में SharedArrayBuffer का उपयोग करते समय, COOP और COEP हेडर आवश्यक होते हैं ताकि क्रॉस-ओरिजिन आइसोलेशन नामक सुरक्षा आवश्यकताओं को पूरा किया जा सके। Node.js में, साझा मेमोरी को worker_threads का उपयोग करके अपेक्षाकृत आसानी से संभाला जा सकता है।
मूलभूत अवधारणाएँ
SharedArrayBuffer एक ऑब्जेक्ट है जो एक निश्चित लंबाई वाले बाइट्स का अनुक्रम दर्शाता है, और आप TypedArray तथा इसी तरह के व्यूज़ के माध्यम से संख्या पढ़ और लिख सकते हैं। साधारण पढ़ने/लिखने के ऑपरेशन सिंक्रनाइज़ नहीं होते, इसलिए आप एटॉमिक ऑपरेशन सुनिश्चित करने के लिए Atomics API का उपयोग करते हैं और समन्वय के लिए wait और notify तंत्रों का उपयोग करते हैं।
सरल काउंटर (ब्राउज़र संस्करण)
इस उदाहरण में, मुख्य थ्रेड एक SharedArrayBuffer बनाता है और उसे Web Worker को देता है, दोनों एक साझा काउंटर को बढ़ाते हैं। यह न्यूनतम पैटर्न को दर्शाता है: Atomics.add के साथ एटॉमिक जोड़ना और Atomics.load के साथ पढ़ना।
main.ts (ब्राउज़र पक्ष)
यह उदाहरण दिखाता है कि मुख्य थ्रेड एक SharedArrayBuffer कैसे बनाता है और मल्टी-थ्रेडेड एक्सेस के लिए उसे एक Worker के साथ साझा करता है।
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};- इस कोड में, मुख्य थ्रेड
Atomics.addका उपयोग करके मान को एटॉमिक रूप से बढ़ाता है।worker.tsपक्ष में, वहीSharedArrayBufferएक्सेस और मैनीपुलेट किया जा सकता है।
worker.ts (ब्राउज़र वर्कर)
यह एक उदाहरण है जहां वर्कर उसी साझा बफर को प्राप्त करता है और समय-समय पर उसे घटाता या मैनीपुलेट करता है।
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};- वर्कर भी
Int32Arrayके माध्यम से उसी मेमोरी को मैनीपुलेट करता है, औरAtomicsके कारण अपडेट्स रेस कंडीशन के बिना होते हैं।
wait/notify का उपयोग करके सिंक्रनाइज़ेशन
Atomics.wait और Atomics.notify का उपयोग करके, आप थ्रेड्स को तब तक निलंबित कर सकते हैं जब तक कोई विशिष्ट शर्तें पूरी न हों, जिससे वर्कर थ्रेड्स में इवेंट-ड्रिवन सिंक्रनाइज़ेशन संभव होता है। ब्राउज़रों में, सबसे सुरक्षित तरीका है कि आप Atomics.wait का उपयोग Worker के अंदर करें।
producer.ts (ब्राउज़र उत्पादक वर्कर)
उत्पादक डेटा लिखता है और 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 (ब्राउज़र उपभोक्ता वर्कर)
उपभोक्ता Atomics.wait के साथ प्रतीक्षा करता है और उत्पादक की सूचना मिलने पर प्रोसेसिंग फिर से शुरू करता है।
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};- इस पैटर्न में, उत्पादक
Atomics.notifyके माध्यम से उपभोक्ता को सूचित करता है और उपभोक्ता प्रभावी ढंग सेAtomics.waitका उपयोग करके प्रतीक्षा करता है। ब्राउज़र में मुख्य थ्रेड परAtomics.waitनहीं बुलाया जा सकता। UI को फ्रीज़ होने से बचाने के लिए,Atomics.waitका उपयोग केवल Workers के भीतर ही सीमित है।
Node.js (worker_threads) के साथ व्यावहारिक उदाहरण
Node.js वातावरण में, आप worker_threads का उपयोग करके SharedArrayBuffer कार्यक्षमता को संभाल सकते हैं। नीचे Node.js के लिए टाइप किया गया TypeScript नमूना है।
main-node.ts
मुख्य थ्रेड बफर बनाता है और उसे 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
इस उदाहरण में Worker पक्ष पर worker_threads से parentPort और workerData का उपयोग किया गया है।
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);- Node.js में ब्राउज़र के जैसे COOP या COEP प्रतिबंध नहीं हैं, इसलिए केवल
worker_threadsका उपयोग करके साझा मेमोरी आसानी से संभाली जा सकती है। TypeScript का उपयोग करते समय, ध्यान दें कि आप CommonJS या ESM सेटिंग्स के साथ बना रहे हैं या नहीं।
TypeScript टाइपिंग बिंदु
SharedArrayBuffer और Atomics मानक DOM और लाइब्रेरी टाइप डिफिनिशन में शामिल हैं, इसलिए आप उन्हें सीधे TypeScript में उपयोग कर सकते हैं। Workers के साथ संदेशों का आदान-प्रदान करते समय इंटरफेस को परिभाषित करना और टाइप्स को स्पष्ट रूप से निर्दिष्ट करना अधिक सुरक्षित होता है।
1// Example: typed message
2type WorkerMessage = { type: 'init'; sab: SharedArrayBuffer } | { type: 'ping' };- टाइप्स को स्पष्ट रूप से परिभाषित करने से
postMessageऔरonmessageका प्रबंधन अधिक सुरक्षित हो जाता है और टाइप चेकिंग संभव होती है।
व्यावहारिक उपयोग मामले
SharedArrayBuffer हमेशा आवश्यक नहीं होता है, लेकिन यह उन परिस्थितियों में बेहद प्रभावी है जहाँ साझा मेमोरी की गति के लाभ आवश्यक होते हैं। यह समझना कि यह किन परिस्थितियों में प्रभावी है, उपयुक्त तकनीकी विकल्पों तक पहुँचने में मदद करता है।
- यह तेज़ साझा बफ़र्स की आवश्यकता वाले कम-विलंबता प्रसंस्करण के लिए उपयुक्त है और रीयल-टाइम ऑडियो/वीडियो प्रोसेसिंग या गेम फिजिक्स में उपयोग किया जा सकता है।
- साधारण डेटा विनिमय या बड़े डेटा ट्रांसफर के लिए,
Transferable ArrayBufferयाpostMessageका उपयोगSharedArrayBufferकी तुलना में आसान हो सकता है।
प्रतिबंध और सुरक्षा
ब्राउज़र में SharedArrayBuffer का उपयोग करने के लिए, क्रॉस-ओरिजिन आइसोलेशन आवश्यक है: COOP को same-origin-allow-popups और COEP को require-corp पर सेट करें। यदि ये आवश्यकताएँ पूरी नहीं होतीं, तो SharedArrayBuffer अक्षम हो जाएगा।
प्रदर्शन और डिबगिंग सुझाव
एटॉमिक ऑपरेशन (Atomics) तेज़ हैं, लेकिन बार-बार प्रतीक्षा और अत्यधिक सिंक्रनाइज़ेशन से विलंबता बढ़ सकती है।
साझा मेमोरी को सुरक्षित और कुशलतापूर्वक संभालने के लिए निम्नलिखित बिंदुओं की जांच करनी चाहिए।
Int32Arrayजैसे व्यूज़ को उचित बाइट अलाइनमेंट के साथ संभालना चाहिए।- साफ़ रखें कि एक ही साझा बफ़र के कौन से इंडेक्स कौन से प्रोसेस द्वारा उपयोग किए जा रहे हैं, और अपने कोड में सतत मानक बनाए रखें।
- अगर आप
SharedArrayBufferको सामान्यArrayBufferकी तरह उपयोग करेंगे, तो डेटा रेस होगी। बिनाAtomicsके कई थ्रेड्स से एक ही इंडेक्स पर लिखना ख़तरनाक है।
सारांश
SharedArrayBuffer और Atomics का उचित उपयोग करके, आप TypeScript में भी सुरक्षित और तेज़ साझा मेमोरी ऑपरेशन कर सकते हैं। काउंटर या सिग्नल जैसे सरल सिंक्रनाइज़ेशन पैटर्न से शुरुआत करना समझने में आसान है, और सही सिंक्रनाइज़ेशन और इंडेक्स का ठीक से प्रबंधन करके, आप कम-विलंबता वाली स्थितियों में भी प्रभावी हो सकते हैं।
आप हमारे YouTube चैनल पर Visual Studio Code का उपयोग करके ऊपर दिए गए लेख के साथ आगे बढ़ सकते हैं। कृपया YouTube चैनल को भी देखें।