Node.js 10.5.0 में थ्रेड्स के साथ काम करने का अभ्यास

हाल ही में, Node.js प्लेटफ़ॉर्म का संस्करण 10.5.0 जारी किया गया है। इसकी मुख्य विशेषताओं में से एक उन धाराओं के साथ काम करने के लिए समर्थन था जो पहली बार Node.js में जोड़े गए थे, जबकि अभी भी प्रयोगात्मक हैं। यह तथ्य इस तथ्य के प्रकाश में विशेष रूप से दिलचस्प है कि मंच के पास अब यह अवसर है, जिसका पालन करने वाले हमेशा इस तथ्य पर गर्व करते रहे हैं कि उसे शानदार एसिंक्रोनस I / O सबसिस्टम के कारण धाराओं की आवश्यकता नहीं है। हालाँकि, Node.js. में थ्रेड समर्थन दिखाई दिया है ऐसा क्यों होगा? वे किसके और क्यों काम आ सकते हैं?



संक्षेप में, यह आवश्यक है ताकि Node.js प्लेटफ़ॉर्म उन क्षेत्रों में नई ऊंचाइयों तक पहुंच सके, जिसमें पहले यह सबसे उल्लेखनीय परिणाम नहीं दिखा था। हम ऐसे अभिकलन करने की बात कर रहे हैं जो प्रोसेसर संसाधनों का गहनता से उपयोग करते हैं। यह मुख्य रूप से कारण है कि Node.js ऐसे क्षेत्रों में बहुत मजबूत नहीं है जैसे कृत्रिम बुद्धिमत्ता, मशीन सीखना और बड़ी मात्रा में डेटा का प्रसंस्करण। Node.js को इस तरह की समस्याओं को हल करने में अच्छी तरह से दिखाने की अनुमति देने की दिशा में बहुत प्रयास किया गया है, लेकिन यहां यह मंच अभी भी बहुत अधिक विनम्र दिखता है, उदाहरण के लिए, माइक्रोसिस्टर्स के विकास में।

सामग्री के लेखक, जिसका अनुवाद आज हम प्रकाशित करते हैं, का कहना है कि उन्होंने तकनीकी दस्तावेज को कम करने का फैसला किया, जो मूल पुल अनुरोध में और आधिकारिक स्रोतों में , सरल व्यावहारिक उदाहरणों के एक सेट में पाया जा सकता है। वह आशा करता है कि जो कोई भी इन उदाहरणों को देखता है, उसे Node.js.

वर्कर_थ्रेड्स मॉड्यूल और -एक्सपेरिमेंटल-वर्कर फ्लैग के बारे में


Node.js में worker_threads समर्थन एक मॉड्यूल worker_threads रूप में कार्यान्वित किया जाता है। इसलिए, नई सुविधा का लाभ उठाने के लिए, इस मॉड्यूल को require कमांड का उपयोग करके जोड़ा जाना चाहिए।

ध्यान दें कि स्क्रिप्ट चलाने के दौरान आप केवल experimental-worker ध्वज का उपयोग करके experimental-worker साथ काम कर सकते हैं, अन्यथा सिस्टम को इस मॉड्यूल नहीं मिलेगा।

ध्यान दें कि ध्वज में "कार्यकर्ता" शब्द शामिल है, न कि "धागा"। बिल्कुल वही, जिसके बारे में हम बात कर रहे हैं, प्रलेखन में उल्लेख किया गया है, जो "वर्कर थ्रेड" (वर्कर थ्रेड) या सिर्फ "वर्कर" (वर्कर) की शर्तों का उपयोग करता है। भविष्य में, हम उसी दृष्टिकोण का पालन करेंगे।

यदि आपने पहले से ही मल्टी-थ्रेडेड कोड लिखा है, तो, Node.js की नई विशेषताओं की खोज करते हुए, आपको बहुत सारी चीजें दिखाई देंगी, जिनसे आप पहले से परिचित हैं। यदि आपने पहले इस तरह से कुछ भी काम नहीं किया है, तो बस आगे पढ़ना जारी रखें, क्योंकि यहां नए लोगों के लिए उपयुक्त स्पष्टीकरण दिए जाएंगे।

उन कार्यों के बारे में जिन्हें Node.js में श्रमिकों की सहायता से हल किया जा सकता है


श्रमिक प्रवाह का इरादा है, जैसा कि पहले ही उल्लेख किया गया है, ऐसे कार्यों को हल करने के लिए जो प्रोसेसर की क्षमताओं का गहनता से उपयोग करते हैं। यह ध्यान दिया जाना चाहिए कि I / O समस्याओं को हल करने के लिए उनका उपयोग संसाधनों की बर्बादी है, क्योंकि आधिकारिक दस्तावेज के अनुसार, आंतरिक Node.js तंत्र अतुल्यकालिक I / O को व्यवस्थित करने के उद्देश्य से उपयोग करने की तुलना में अपने आप में बहुत अधिक कुशल हैं। कार्यकर्ता प्रवाह की इसी समस्या को हल करना। इसलिए, हम तुरंत निर्णय लेते हैं कि हम श्रमिकों का उपयोग करके डेटा के इनपुट और आउटपुट के साथ सौदा नहीं करेंगे।

आइए एक सरल उदाहरण से शुरू करें जो यह दर्शाता है कि श्रमिकों को कैसे बनाया जाए और उनका उपयोग कैसे किया जाए।

उदाहरण संख्या १


 const { Worker, isMainThread,  workerData } = require('worker_threads'); let currentVal = 0; let intervals = [100,1000, 500] function counter(id, i){   console.log("[", id, "]", i)   return i; } if(isMainThread) {   console.log("this is the main thread")   for(let i = 0; i < 2; i++) {       let w = new Worker(__filename, {workerData: i});   }   setInterval((a) => currentVal = counter(a,currentVal + 1), intervals[2], "MainThread"); } else {   console.log("this isn't")   setInterval((a) => currentVal = counter(a,currentVal + 1), intervals[workerData], workerData); } 

इस कोड का आउटपुट काउंटरों को दर्शाने वाली रेखाओं के समूह की तरह दिखेगा, जिनके मूल्य अलग-अलग गति से बढ़ते हैं।


पहले उदाहरण के परिणाम

हम यहां जो हो रहा है उससे निपटेंगे:

  1. if अभिव्यक्ति के अंदर के निर्देश 2 थ्रेड बनाते हैं, तो कोड, जिसके लिए, __filename पैरामीटर के लिए धन्यवाद, उसी स्क्रिप्ट से लिया जाता है जो कि उदाहरण के लिए Node.js पास हुआ था। अब श्रमिकों को कोड के साथ फ़ाइल के लिए पूर्ण पथ की आवश्यकता है, वे सापेक्ष पथों का समर्थन नहीं करते हैं, यही कारण है कि इस मूल्य का उपयोग यहां किया जाता है।
  2. इन दो श्रमिकों के लिए डेटा को वैश्विक पैरामीटर के रूप में, workerData विशेषता के रूप में भेजा जाता है, जिसका उपयोग दूसरे तर्क में किया जाता है। उसके बाद, इस मान तक पहुंच उसी नाम के साथ एक स्थिरांक के माध्यम से प्राप्त की जा सकती है (ध्यान दें कि फ़ाइल की पहली पंक्ति में संबंधित स्थिरांक को कैसे बनाया जाता है, और अंतिम पंक्ति में इसका उपयोग कैसे किया जाता है)।

यहां worker_threads मॉड्यूल का उपयोग करने का एक बहुत ही सरल उदाहरण है, यहां अभी तक कुछ भी दिलचस्प नहीं है। इसलिए, एक और उदाहरण पर विचार करें।

उदाहरण संख्या 2


एक उदाहरण पर विचार करें जिसमें, सबसे पहले, हम कुछ "भारी" गणना करेंगे, और दूसरी बात, मुख्य धागे में कुछ अतुल्यकालिक करें।

 const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); const request = require("request"); if(isMainThread) {   console.log("This is the main thread")   let w = new Worker(__filename, {workerData: null});   w.on('message', (msg) => { //  !       console.log("First value is: ", msg.val);       console.log("Took: ", (msg.timeDiff / 1000), " seconds");   })   w.on('error', console.error);   w.on('exit', (code) => {       if(code != 0)           console.error(new Error(`Worker stopped with exit code ${code}`))   });   request.get('http://www.google.com', (err, resp) => {       if(err) {           return console.error(err);       }       console.log("Total bytes received: ", resp.body.length);   }) } else { //    function random(min, max) {       return Math.random() * (max - min) + min   }   const sorter = require("./list-sorter");   const start = Date.now()   let bigList = Array(1000000).fill().map( (_) => random(1,10000))   sorter.sort(bigList);   parentPort.postMessage({ val: sorter.firstValue, timeDiff: Date.now() - start}); } 

इस उदाहरण को चलाने के लिए, इस तथ्य पर ध्यान दें कि इस कोड के लिए request मॉड्यूल की आवश्यकता है (इसे npm init --yes का उपयोग करके स्थापित किया जा सकता है, उदाहरण के लिए, npm init --yes और npm init --yes कमांड का उपयोग करके खाली निर्देशिका में उपरोक्त कोड वाली फ़ाइल के साथ। npm install request --save ), और यह तथ्य कि यह सहायक मॉड्यूल का उपयोग करता है, जो कमांड npm install request --save से जुड़ा है const sorter = require("./list-sorter"); । इस मॉड्यूल ( list-sorter.js ) की फ़ाइल ऊपर बताई गई फ़ाइल की तरह ही होनी चाहिए, इसका कोड इस तरह दिखता है:

 module.exports = {   firstValue: null,   sort: function(list) {       let sorted = list.sort();       this.firstValue = sorted[0]   } } 

इस बार हम एक साथ दो समस्याओं को हल कर रहे हैं। सबसे पहले, हम google.com होमपेज को लोड करते हैं, और दूसरी बात, हम एक मिलियन संख्याओं के यादृच्छिक रूप से उत्पन्न सरणी को सॉर्ट करते हैं। इसमें कुछ सेकंड लग सकते हैं, जो हमें कार्रवाई में नए Node.js तंत्र को देखने का एक शानदार अवसर देता है। इसके अलावा, यहां हम वर्कर थ्रेड को संख्याओं को क्रमबद्ध करने में लगने वाले समय को मापते हैं, जिसके बाद हम माप परिणाम (सॉर्ट किए गए एरे के पहले तत्व के साथ) को मुख्य स्ट्रीम में भेजते हैं, जो कंसोल में रिजल्ट प्रदर्शित करता है।


दूसरे उदाहरण का परिणाम

इस उदाहरण में, सबसे महत्वपूर्ण बात यह है कि थ्रेड्स के बीच डेटा विनिमय के तंत्र का प्रदर्शन करना है।
श्रमिक मुख्य थ्रेड से on पद्धति के लिए संदेश प्राप्त कर सकते हैं। कोड में आप उन घटनाओं को पा सकते हैं जिन्हें हम सुन रहे हैं। हर बार जब हम parentPort.postMessage मेथड का उपयोग करके एक निश्चित थ्रेड से एक संदेश भेजते हैं तो message घटना को message जाता है। इसके अलावा, एक ही विधि का उपयोग एक कार्यकर्ता उदाहरण पर parentPort एक संदेश भेजने के लिए किया जा सकता है और parentPort ऑब्जेक्ट का उपयोग करके उन्हें प्राप्त किया जा parentPort है।

अब एक और उदाहरण देखते हैं, जो हम पहले ही देख चुके हैं, इसके समान है, लेकिन इस बार हम परियोजना की संरचना पर विशेष ध्यान देंगे।

उदाहरण संख्या 3


अंतिम उदाहरण के रूप में, हम पिछले उदाहरण की तरह ही कार्यक्षमता के कार्यान्वयन पर विचार करने का प्रस्ताव करते हैं, लेकिन इस बार हम कोड की संरचना में सुधार करेंगे, इसे क्लीनर बनाएंगे, इसे एक ऐसे रूप में लाएंगे जो एक सॉफ्टवेयर परियोजना का समर्थन करने की सुविधा को बढ़ाता है।

यहाँ मुख्य कार्यक्रम के लिए कोड है।

 const { Worker, isMainThread, parentPort, workerData } = require('worker_threads'); const request = require("request"); function startWorker(path, cb) {   let w = new Worker(path, {workerData: null});   w.on('message', (msg) => {       cb(null, msg)   })   w.on('error', cb);   w.on('exit', (code) => {       if(code != 0)           console.error(new Error(`Worker stopped with exit code ${code}`))  });   return w; } console.log("this is the main thread") let myWorker = startWorker(__dirname + '/workerCode.js', (err, result) => {   if(err) return console.error(err);   console.log("[[Heavy computation function finished]]")   console.log("First value is: ", result.val);   console.log("Took: ", (result.timeDiff / 1000), " seconds"); }) const start = Date.now(); request.get('http://www.google.com', (err, resp) => {   if(err) {       return console.error(err);   }   console.log("Total bytes received: ", resp.body.length);   //myWorker.postMessage({finished: true, timeDiff: Date.now() - start}) //     }) 

और यहां वह कोड है जो वर्कर थ्रेड के व्यवहार का वर्णन करता है (उपरोक्त प्रोग्राम में, इस कोड के साथ फाइल का पथ __dirname + '/workerCode.js' का उपयोग करके __dirname + '/workerCode.js' ):

 const {  parentPort } = require('worker_threads'); function random(min, max) {   return Math.random() * (max - min) + min } const sorter = require("./list-sorter"); const start = Date.now() let bigList = Array(1000000).fill().map( (_) => random(1,10000)) /** //      : parentPort.on('message', (msg) => {   console.log("Main thread finished on: ", (msg.timeDiff / 1000), " seconds..."); }) */ sorter.sort(bigList); parentPort.postMessage({ val: sorter.firstValue, timeDiff: Date.now() - start}); 

इस उदाहरण की विशेषताएं इस प्रकार हैं:

  1. अब मुख्य धागे के लिए और श्रमिक धागे के लिए कोड अलग-अलग फ़ाइलों में स्थित है। यह परियोजना के समर्थन और विस्तार की सुविधा प्रदान करता है।
  2. startWorker फ़ंक्शन कार्यकर्ता की एक नई आवृत्ति लौटाता है, जो आवश्यक होने पर, मुख्य धारा से इस कार्यकर्ता को संदेश भेजने की अनुमति देता है।
  3. यह जांचने की कोई आवश्यकता नहीं है कि क्या कोड मुख्य थ्रेड में निष्पादित हो रहा है (हमने संबंधित चेक के साथ if को हटा दिया है)।
  4. कार्यकर्ता मुख्य धारा से संदेश प्राप्त करने के लिए तंत्र का प्रदर्शन करते हुए एक टिप्पणी-आउट कोड टुकड़ा दिखाता है, जो पहले से ही चर्चा किए गए संदेश भेजने वाले तंत्र को देखते हुए, मुख्य धारा और कार्यकर्ता धारा के बीच दो-तरफा अतुल्यकालिक डेटा विनिमय की अनुमति देता है।

परिणाम


इस लेख में, हमने व्यावहारिक उदाहरणों का उपयोग करते हुए, Node.js. में प्रवाह के साथ काम करने के लिए नई क्षमताओं के उपयोग की सुविधाओं की जांच की। यदि आपको यहां पर चर्चा करने में महारत हासिल है, तो इसका मतलब है कि आप अपने प्रलेखन को देखने के लिए तैयार हैं और अपने स्वयं के प्रयोगों को worker_threads मॉड्यूल के साथ शुरू कर रहे हैं। शायद यह ध्यान देने योग्य है कि यह सुविधा केवल Node.js में दिखाई देती है, जबकि यह प्रयोगात्मक है, इसलिए समय के साथ, इसके कार्यान्वयन में कुछ बदल सकता है। इसके अलावा, यदि आपके पास worker_threads साथ अपने स्वयं के प्रयोगों के worker_threads आप त्रुटियों का सामना करते हैं, या पाते हैं कि यह मॉड्यूल इससे गायब होने वाली कुछ विशेषता के साथ हस्तक्षेप नहीं करता है, तो डेवलपर्स को इसके बारे में बताएं और Node.js. प्लेटफ़ॉर्म को बेहतर बनाने में मदद करें।

प्रिय पाठकों! Node.js में बहुपरत समर्थन से आप क्या समझते हैं? क्या आप अपनी परियोजनाओं में इस सुविधा का उपयोग करने की योजना बना रहे हैं?

Source: https://habr.com/ru/post/hi415659/


All Articles