node.js에서 병렬 실행 조정


79

node.js의 이벤트 중심 프로그래밍 모델은 프로그램 흐름을 조정하는 데 다소 까다로워집니다.

간단한 순차 실행은 중첩 된 콜백으로 바뀌며, 이는 충분히 쉽습니다 (기록하기에는 약간 복잡함).

그러나 병렬 실행은 어떻습니까? 병렬로 실행할 수있는 세 개의 작업 A, B, C가 있고 작업이 완료되면 그 결과를 작업 D로 보내려고합니다.

포크 / 조인 모델을 사용하면

  • 포크 A
  • 포크 B
  • 포크 C
  • A, B, C 가입, D 실행

node.js에 어떻게 작성합니까? 모범 사례 나 요리 책이 있습니까? 매번 솔루션직접 굴려야 합니까 , 아니면이를위한 도우미가있는 라이브러리가 있습니까?

답변:


128

단일 스레드이기 때문에 node.js에서는 실제로 병렬이 아닙니다. 그러나 여러 이벤트를 예약하고 미리 결정할 수없는 순서대로 실행할 수 있습니다. 그리고 데이터베이스 액세스와 같은 일부는 데이터베이스 쿼리 자체가 별도의 스레드에서 실행되지만 완료되면 이벤트 스트림에 다시 통합된다는 점에서 실제로 "병렬"입니다.

그렇다면 여러 이벤트 핸들러에서 콜백을 어떻게 예약합니까? 글쎄, 이것은 브라우저 측 자바 스크립트의 애니메이션에서 사용되는 일반적인 기술 중 하나입니다. 변수를 사용하여 완료를 추적하십시오.

이것은 해킹처럼 들리며 그것은 잠재적으로 지저분하게 들리며 추적을 수행하는 데 많은 전역 변수를 남기고 더 적은 언어로 될 것입니다. 하지만 자바 스크립트에서는 클로저를 사용할 수 있습니다.

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);

위의 예에서는 비동기 및 콜백 함수에 인수가 필요하지 않다고 가정하여 코드를 단순하게 유지합니다. 물론 코드를 수정하여 비동기 함수에 인수를 전달하고 콜백 함수가 결과를 누적하여 shared_callback 함수에 전달하도록 할 수 있습니다.


추가 답변 :

실제로,이 fork()함수는 이미 클로저를 사용하여 비동기 함수에 인수를 전달할 수 있습니다.

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

남은 일은 A, B, C의 결과를 모아 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 또는 다양한 promise 기반 라이브러리와 같은 라이브러리가 존재하기 전에 작성되었습니다. 나는 async.js가 이것에 의해 영감을 받았다고 믿고 싶지만 증거가 없습니다. 어쨌든 .. 오늘 이것을 할 생각이라면 async.js 또는 promise를 살펴보십시오. async.parallel과 같은 것이 어떻게 작동하는지에 대한 좋은 설명 / 그림 위의 대답을 고려하십시오.

완전성을 위해 다음은 사용 방법입니다 async.parallel.

var async = require('async');

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

위에서 구현 한 함수 async.parallel와 정확히 동일 하게 작동합니다 fork. 가장 큰 차이점은 Dnode.js 규칙에 따라 첫 번째 인수로 오류를 전달 하고 두 번째 인수로 콜백을 전달한다는 것 입니다.

promise를 사용하여 다음과 같이 작성합니다.

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

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

12
"단일 스레드이기 때문에 node.js에서 진정한 병렬은 없습니다." 사실이 아니다. CPU를 사용하지 않는 모든 것 (예 : 네트워크 I / O 대기)은 병렬로 실행됩니다.
Thilo 2011 년

3
대부분 사실입니다. Node에서 IO를 기다리는 것은 다른 코드의 실행을 차단하지 않지만 코드가 실행될 때 한 번에 하나씩입니다. Node의 유일한 병렬 실행은 자식 프로세스를 생성하는 것이지만 거의 모든 환경에 대해 말할 수 있습니다.
MooGoo 2011 년

6
@Thilo : 일반적으로 CPU를 실행하지 않는 것으로 사용하지 않는 코드를 호출합니다. 실행하지 않는 경우 병렬로 "실행"할 수 없습니다.
slebetman 2011 년

4
@MooGoo : 이것이 의미하는 바는 이벤트를 사용하면 확실히 병렬로 실행할 수 없다는 것을 알고 있기 때문에 세마포어와 뮤텍스에 대해 걱정할 필요가없는 반면 스레드에서는 공유 리소스를 잠 가야합니다.
slebetman 2011 년

2
이것이 병렬로 실행되는 함수는 아니지만 각 'async_func'가 반환 될 때까지 진행되지 않는 코드로 결정되지 않은 시퀀스로 실행된다는 점이 맞습니까?
Aaron Rustad 2011 년

10

이제 "async"모듈이이 병렬 기능을 제공하며 위의 fork 기능과 거의 동일하다고 생각합니다.


2
이것은 올바르지 않습니다. 비동기는 단일 프로세스 내에서 코드 흐름을 구성하는 데만 도움이됩니다.
bwindels

2
async.parallel은 실제로 위 fork함수 와 거의 동일한 작업을 수행합니다.
Dave Stibrany 2013 년

그것은 진정한 병렬 아니다
RAB

5

선물 모듈라는 서브 모듈을 가지고 참여 내가 사용에 좋아하는 것을 :

pthread_join스레드의 작동 방식 과 유사하게 비동기 호출을 함께 결합 합니다.

Readme는 프리 스타일을 사용하거나 Promise 패턴을 사용하는 미래 서브 모듈을 사용하는 좋은 예를 보여줍니다 . 문서의 예 :

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가 모두 동일한 콜백 함수를 공유하도록하고, 해당 함수에 전역 또는 적어도 기능 외 증분자를 갖도록하는 것입니다. 물론 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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.