потоки libuv выполняются последовательно в nodejs

Итак, я выполнял этот код, чтобы проверить количество потоков в пуле потоков, созданном библиотекой libuv:

var fs = require('fs');
var util = require('util');
var sleep = require('sleep');
var start = process.hrtime();

var sample_func = function(callback) {
  var i = 0;
  sleep.sleep(1);
  callback();
}
for (var i = 0; i < 6; ++i) {
  (function (id) {
    sample_func(function () {
      var end = process.hrtime(start);
      console.log(util.format('sample func %d finished in %ds', id, end[0] + end[1] / 1e9));
    });
  })(i);
}

Поскольку libuv по умолчанию создает 4 потока в пуле потоков, а моя примерная функция является асинхронной, я ожидал, что этот вывод будет напечатан в моей консоли:

sample func 0 finished in 1.003170344s
sample func 1 finished in 1.052704191s
sample func 2 finished in 1.058100525s
sample func 3 finished in 1.060514229s
sample func 4 finished in 2.003446385s
sample func 5 finished in 2.007682862s

Таким образом, первые 4 итерации выполняются параллельно, а последние 2 должны ждать завершения первых 4. Однако то, что я получил, было это -

sample func 0 finished in 1.00095422s
sample func 1 finished in 2.056155718s
sample func 2 finished in 3.058480649s
sample func 3 finished in 4.061336076s
sample func 4 finished in 5.063556904s
sample func 5 finished in 6.066219487s

Это означает, что каждая итерация выполняется одна за другой, что занимает в общей сложности 6 секунд. Может ли кто-нибудь помочь мне здесь, почему это происходит?

PS: я напечатал значение process.env.UV_THREADPOOL_SIZE, я получил неопределенное значение; версия узла: v12.9.1

Спасибо


person Akshat Gupta    schedule 27.02.2020    source источник
comment
sleep блоков на секунду, синхронно, на каждой итерации. Не уверен насчет libuv, вам придется вызывать параллельную обработку каким-то другим способом (child_process?)   -  person CertainPerformance    schedule 27.02.2020
comment
Node.js использует только один поток для выполнения кода. Он использует один и тот же поток (а не отдельный поток) для сетевого ввода-вывода. Он только запускает дополнительные потоки для обработки дискового ввода-вывода, DNS (который обычно не имеет неблокирующего API) и некоторых криптографических функций (потому что это вычисления, а не ввод-вывод). Node.js и javascript в браузере по сути однопоточные. Это асинхронный/событийный/неблокирующий, но однопоточный (вы обнаружите, что в других языках неблокирующий код, как правило, однопоточный). Он может ждать параллельно, но выполнять вычисления последовательно.   -  person slebetman    schedule 27.02.2020
comment
Насколько я понял, все асинхронные функции выполняются потоками в пуле потоков, затем обратные вызовы ставятся в очередь задач, которые выбираются циклом событий один за другим для выполнения в основном потоке. Поправьте меня, если я ошибаюсь, пожалуйста.   -  person Akshat Gupta    schedule 27.02.2020


Ответы (1)


Цикл событий Nodejs (реализованный Libuv) выполняется в одном потоке (основном потоке). Код блокировки (в вашем случае sleep()) выполняется в основном потоке. Сетевые, файловые и DNS-операции ввода-вывода выполняются Libuv асинхронно. Nodejs используют следующую схему:

API-интерфейсы асинхронной системы используются Node.js везде, где это возможно, но там, где они не существуют, пул потоков libuv используется для создания API-интерфейсов асинхронных узлов на основе API-интерфейсов синхронной системы. API-интерфейсы Node.js, использующие пул потоков:

  • все API-интерфейсы fs, кроме API-интерфейсов файлового наблюдателя и тех, которые:
  • явно синхронные асинхронные криптографические API, такие как crypto.pbkdf2(), crypto.scrypt(), crypto.randomBytes(), crypto.randomFill(), crypto.generateKeyPair()
  • dns.lookup() все zlib *API, кроме явно синхронных.

Теперь возвращаясь к вашей проблеме, цикл событий функционального блока sleep.sleep(1); (основной поток) в течение 1 секунды, как говорится в документации sleep. Вот почему ваша функция запускается одна за другой синхронно.

person Sandeep Patel    schedule 27.02.2020