2014-08-07 16 views
3

Я пытаюсь изучить параллельное выполнение в node.js. Я написал ниже пример кода. Однако выход последовательный. Сначала печатается 0..99, а затем 100..200.node.js параллельное выполнение

Я понимаю, это потому, что node.js по сути является однопоточным и внутри цикла, поток захватывается циклом for.

Что я пытаюсь понять, в каких случаях эта структура flow.parallel полезна? Любой запрос к I/O или базе данных будет в любом случае асинхронным в node.js. Тогда зачем нам flow.parallel?

var flow = require('nimble'); 


flow.parallel([ 

    function a(callback) 
    { 
     for(var i=0;i<100;++i) 
     { 
      console.log(i); 

     } 
      callback(); 
    }, 
    function b(callback) 
    { 

     for (var i=100;i<200;++i) 
     { 
      console.log(i); 

     } 
     callback(); 
    } 
    ]); 
+0

Это полезно для выполнения чего-либо после всех параллельных задач. Реальная проблема с async - это не запускать что-то параллельно, а запускать что-то после чего-то другого. –

+0

Боковое примечание: похоже, что caolan не обновил проворство через 3 года. Вместо этого вы можете проверить caolan/highland. –

ответ

7

В большинстве случаев с помощью параллельного потока, такие как это, вы не будете печатать кучу цифр в для цикла (который так случается, блокирование выполнения). Когда вы регистрируете свои функции, они регистрируются в том же порядке, в котором вы определили их в этом массиве, переходя к parallel. В приведенном выше случае function a сначала и function b секунд. Следовательно, цикл событий Node вызовет a(), а затем b() в нераскрытое время позже. Поскольку мы знаем, что блоки for-loops блокируются, а узел работает в одном потоке, он должен завершить весь цикл for в пределах a() и, наконец, вернуться до того, как цикл событий Node снова получит контроль над ним, где b() ждет в очереди для процессов аналогичным образом.

Почему используется параллельная конструкция управления потоком? По дизайну вы не должны делать блокирующие операции внутри узла (см. Ваш пример). a() потребляет всю нить, тогда b() будет потреблять всю цепочку, прежде чем что-нибудь еще произойдет.

a() b() 
| 
| 
| 
| 
RET 
    | 
    | 
    | 
    | 
    RET 

Теперь предположим, что вы делаете веб-приложение, в котором пользователь может зарегистрироваться и, в то же время, загрузить картинку. Ваша регистрация пользователя может иметь такой код:

var newUser = { 
    username: 'bob', 
    password: '...', 
    email: '[email protected]', 
    picture: '20140806-210743.jpg' 
} 

var file = path.join(img.IMG_STORE_DIR, newUser.picture); 

flow.parallel([ 
    function processImage(callback) { 
    img.process(function (err) { 
     if (err) return callback(err); 

     img.save(file, function (err) { 
     return callback(err); // err should be falsey if everything was good 
     }) 
    }); 
    }, 
    function dbInsert(callback) { 
    db.doQuery('insert', newUser, function (err, id) { 
     return callback(err); 
    }); 
    } 
], function() { 
    // send the results to the user now to let them know they are all registered! 
}); 

Внутренние функции здесь без блокировки, и оба вызова при обработке или сетевыми нагруженных операциях. Они, однако, довольно независимы друг от друга. Вам не нужно, чтобы кто-то закончил, чтобы другой начал. Внутри функций, которые мы не видим для кода, они используют больше асинхронных вызовов с обратными вызовами функций, каждый из которых вводит другой элемент для обработки узла. Узел попытается очистить очередь, равномерно распределив рабочую нагрузку между циклами ЦП.

Мы надеемся, что что-то подобное происходит сейчас:

a = processImage 
b = dbInsert 
a() b() 
| 
     | 
| 
     | 
| 
     | 
| 
RET | 
    RET 

Если бы мы имели их последовательно, то есть вы должны ждать, пока изображение полностью обработан до дб вставки, вы должны сделать много ожиданий. Если IO действительно высока в вашей системе, узел будет чередоваться с его большими пальцами, ожидающими в ОС. В отличие от этого, используя параллель, теоретически это позволит медленным операциям свести к более быстрым.

Если Узел делает это сам по себе, зачем нам это действительно нужно? Ключ находится во втором аргументе, который вы опустили.

nimble.parallel([a,b], function() { 
    // both functions have now returned and called-back. 
}); 

Теперь вы можете видеть, когда обе задачи выполняются, узел не делает это по умолчанию, так что это может быть довольно полезная вещь, чтобы иметь.

2

flow.parallel дает логику многократного использования для определения того, когда все параллельные операции завершены.Да, если вы только что сделали db.query('one');db.query('two');db.query('three');, все они будут выполняться параллельно по характеру async, но вам придется написать какой-нибудь шаблонный код, чтобы отслеживать, когда все это делается, и если кто-то столкнулся с ошибкой. Это та часть, которую предоставляет flow.parallel (или ее аналог в любой библиотеке управления потоками).

Смежные вопросы