नोड.जेएस में समानांतर निष्पादन का समन्वय


79

नोड-जेएस के घटना-संचालित प्रोग्रामिंग मॉडल कार्यक्रम के प्रवाह को समन्वयित करने के लिए इसे कुछ हद तक मुश्किल बना देता है।

सरल अनुक्रमिक निष्पादन नेस्टेड कॉलबैक में बदल जाता है, जो कि काफी आसान है (हालांकि नीचे लिखने के लिए थोड़ा जटिल है)।

लेकिन समानांतर निष्पादन के बारे में कैसे? मान लें कि आपके पास तीन कार्य A, B, C हैं जो समानांतर में चल सकते हैं और जब वे पूर्ण हो जाते हैं, तो आप उनके परिणामों को टास्क D पर भेजना चाहते हैं।

फोर्क / जॉइन मॉडल के साथ यह होगा

  • कांटा ए
  • कांटा बी
  • कांटा सी
  • A, B, C से जुड़ें, D को चलाएँ

मैं इसे n.js में कैसे लिखूं? क्या कोई सर्वोत्तम अभ्यास या कुकबुक हैं? क्या मुझे हर बार एक समाधान हाथ से करना है , या इसके लिए सहायकों के साथ कुछ पुस्तकालय है?

जवाबों:


128

कुछ भी सही मायने में नोड में समानांतर नहीं है। क्योंकि यह एकल पिरोया हुआ है। हालाँकि, कई ईवेंट्स को शेड्यूल किया जा सकता है और एक क्रम में चलाया जा सकता है जिसे आप पहले से निर्धारित नहीं कर सकते हैं। और डेटाबेस एक्सेस जैसी कुछ चीजें वास्तव में "समानांतर" होती हैं, जिसमें डेटाबेस क्वेरीज़ स्वयं अलग थ्रेड्स में चलाई जाती हैं, लेकिन पूरा होने पर इवेंट स्ट्रीम में पुनः एकीकृत हो जाती हैं।

तो, आप कई ईवेंट हैंडलर्स पर कॉलबैक कैसे शेड्यूल करते हैं? खैर, यह ब्राउज़र साइड जावास्क्रिप्ट में एनिमेशन में उपयोग की जाने वाली एक सामान्य तकनीक है: पूर्णता को ट्रैक करने के लिए एक चर का उपयोग करें।

यह एक हैक की तरह लगता है और यह है, और यह संभावित रूप से गड़बड़ लगता है कि ट्रैकिंग करने के आसपास वैश्विक चर का एक गुच्छा छोड़ने और कम भाषा में यह होगा। लेकिन जावास्क्रिप्ट में हम क्लोजर का उपयोग कर सकते हैं:

function fork (async_calls, shared_callback) {
  var counter = async_calls.length;
  var callback = function () {
    counter --;
    if (counter == 0) {
      shared_callback()
    }
  }

  for (var i=0;i<async_calls.length;i++) {
    async_calls[i](callback);
  }
}

// usage:
fork([A,B,C],D);

ऊपर दिए गए उदाहरण में हम async मानकर कोड को सरल रखते हैं और कॉलबैक फ़ंक्शन को किसी तर्क की आवश्यकता नहीं है। आप तर्क को async फ़ंक्शंस में पास करने के लिए कोड को संशोधित कर सकते हैं और कॉलबैक फ़ंक्शन के परिणाम संचित कर सकते हैं और इसे साझा करने के लिए share_callback फ़ंक्शन पास कर सकते हैं।


अतिरिक्त उत्तर:

वास्तव में, जैसा कि है, वह fork()फ़ंक्शन पहले से ही एक बंद का उपयोग करके async कार्यों के लिए तर्क पारित कर सकता है:

fork([
  function(callback){ A(1,2,callback) },
  function(callback){ B(1,callback) },
  function(callback){ C(1,2,callback) }
],D);

एकमात्र काम करना बाकी है कि ए, बी, सी से परिणाम जमा करें और उन्हें डी पर पास करें।


और भी अधिक उत्तर:

मैं विरोध नहीं कर सका। नाश्ते के दौरान इस बारे में सोचते रहे। यहां इसका एक कार्यान्वयन fork()परिणाम जमा करता है (आमतौर पर कॉलबैक फ़ंक्शन के तर्क के रूप में पारित किया जाता है):

function fork (async_calls, shared_callback) {
  var counter = async_calls.length;
  var all_results = [];
  function makeCallback (index) {
    return function () {
      counter --;
      var results = [];
      // we use the arguments object here because some callbacks 
      // in Node pass in multiple arguments as result.
      for (var i=0;i<arguments.length;i++) {
        results.push(arguments[i]);
      }
      all_results[index] = results;
      if (counter == 0) {
        shared_callback(all_results);
      }
    }
  }

  for (var i=0;i<async_calls.length;i++) {
    async_calls[i](makeCallback(i));
  }
}

यह काफी आसान था। यह बनाता हैfork() काफी सामान्य उद्देश्य और इसका उपयोग कई गैर-सजातीय घटनाओं को सिंक्रनाइज़ करने के लिए किया जा सकता है।

Node.js में उदाहरण उपयोग:

// Read 3 files in parallel and process them together:

function A (c){ fs.readFile('file1',c) };
function B (c){ fs.readFile('file2',c) };
function C (c){ fs.readFile('file3',c) };
function D (result) {
  file1data = result[0][1];
  file2data = result[1][1];
  file3data = result[2][1];

  // process the files together here
}

fork([A,B,C],D);

अपडेट करें

यह कोड async.js जैसे पुस्तकालयों के अस्तित्व या विभिन्न वादों पर आधारित पुस्तकालयों के अस्तित्व से पहले लिखा गया था। मुझे विश्वास है कि async.js इससे प्रेरित था, लेकिन मेरे पास इसका कोई सबूत नहीं है। वैसे भी .. अगर आप आज ऐसा करने की सोच रहे हैं तो async.js या वादों पर एक नज़र डालें। बस एक अच्छी व्याख्या / विवरण के ऊपर विचार करें कि कैसे काम करते हैं।

पूर्णता के लिए निम्नलिखित है कि आप इसे कैसे करेंगे async.parallel:

var async = require('async');

async.parallel([A,B,C],D);

ध्यान दें कि async.parallelठीक उसी तरह काम करता है जैसा forkहमने ऊपर लागू किया था। मुख्य अंतर यह है कि पहले तर्क के रूप में एक त्रुटि पारित होती हैD और नोड तर्क के अनुसार दूसरे तर्क के रूप में कॉलबैक को पारित करता है।

वादों का उपयोग करते हुए, हम इसे इस प्रकार लिखेंगे:

// Assuming A, B & C return a promise instead of accepting a callback

Promise.all([A,B,C]).then(D);

12
"नोड थ्रेड में कुछ भी सही मायने में समानांतर नहीं है क्योंकि यह सिंगल थ्रेडेड है।" सच नहीं। सब कुछ जो सीपीयू का उपयोग नहीं करता है (जैसे कि नेटवर्क I / O की प्रतीक्षा) समानांतर में चलता है।
थिलो

3
यह सच है, अधिकांश भाग के लिए। नोड में IO की प्रतीक्षा करना अन्य कोड को चलाने से नहीं रोकता है, लेकिन जब कोड चलाया जाता है, तो यह एक समय में एक होता है। नोड में एकमात्र वास्तविक समानांतर निष्पादन बच्चे की प्रक्रिया से होता है, लेकिन तब लगभग किसी भी पर्यावरण के बारे में कहा जा सकता है।
मूओगू

6
@ थिलो: आमतौर पर हम उस कोड को कहते हैं जो सीपीयू का उपयोग नहीं करता है क्योंकि वह नहीं चल रहा है। यदि आप नहीं चल रहे हैं तो आप समानांतर में "रनिंग" नहीं कर सकते।
स्लीपबेटमैन

4
@MooGoo: इसका निहितार्थ यह है कि घटनाओं के साथ, क्योंकि हम जानते हैं कि वे निश्चित रूप से समानांतर में नहीं चल सकते हैं, हमें सेमीफॉर्स और म्यूटेक्स के बारे में चिंता करने की ज़रूरत नहीं है जबकि थ्रेड्स के साथ हमें साझा संसाधनों को लॉक करना होगा।
स्लीपबेटमैन sle

2
क्या मैं यह कहने में सही हूं कि ये समानांतर में निष्पादित करने वाले कार्य नहीं हैं, लेकिन वे (सर्वोत्तम में) कोड के साथ एक अनिर्धारित अनुक्रम में निष्पादित कर रहे हैं जब तक कि प्रत्येक 'async_func' रिटर्न न हो?
आरोन रुस्तद

10

मेरा मानना ​​है कि अब "async" मॉड्यूल इस समानांतर कार्यक्षमता प्रदान करता है और लगभग कांटा फ़ंक्शन के समान है।


2
यह गलत है, async केवल एक प्रक्रिया के भीतर आपके कोड प्रवाह को व्यवस्थित करने में आपकी मदद करता है।
बवंडेल्स

2
async.par समानांतर वास्तव में उपरोक्त forkकार्य के रूप में लगभग एक ही काम करता है
डेव स्टेब्रानी

यह एक वास्तविक समानता नहीं है
रब

5

वायदा मॉड्यूल एक submodule कहा जाता है में शामिल होने कि मैं उपयोग करने के लिए पसंद किया है:

pthread_joinधागे के लिए कैसे काम करता है के समान एक साथ अतुल्यकालिक कॉल को जोड़ता है ।

रीडमी इसे फ्रीस्टाइल का उपयोग करने या प्रोमिस पैटर्न का उपयोग करके भविष्य के सबमॉड्यूल का उपयोग करने के कुछ अच्छे उदाहरण दिखाता है । डॉक्स से उदाहरण:

var Join = require('join')
  , join = Join()
  , callbackA = join.add()
  , callbackB = join.add()
  , callbackC = join.add();

function abcComplete(aArgs, bArgs, cArgs) {
  console.log(aArgs[1] + bArgs[1] + cArgs[1]);
}

setTimeout(function () {
  callbackA(null, 'Hello');
}, 300);

setTimeout(function () {
  callbackB(null, 'World');
}, 500);

setTimeout(function () {
  callbackC(null, '!');
}, 400);

// this must be called after all 
join.when(abcComplete);

2

एक सरल समाधान यहां संभव हो सकता है: http://howtonode.org/control-flow-part-ii स्क्रॉल को समानांतर क्रियाओं के लिए। एक और तरीका यह होगा कि A, B और C सभी समान कॉलबैक फ़ंक्शन साझा करें, उस फ़ंक्शन के पास एक वैश्विक या कम से कम आउट-ऑफ़-फ़ंक्शन इंक्रीमेंटर है, यदि तीनों ने कॉलबैक कहा है, तो इसे डी चलाएं, संभोग आपको ए, बी, और सी के परिणामों को कहीं और संग्रहीत करना होगा।




0

लोकप्रिय वादों और एसिंक्स-लाइब्रेरी के अलावा, तीसरा सुरुचिपूर्ण तरीका है - "वायरिंग" का उपयोग करना:

var l = new Wire();

funcA(l.branch('post'));
funcB(l.branch('comments'));
funcC(l.branch('links'));

l.success(function(results) {
   // result will be object with results:
   // { post: ..., comments: ..., links: ...}
});

https://github.com/garmoshka-mo/mo-wire

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.