Pull to refresh

Comments 39

Всегда ассинхронный node можно трансформировать в синхронность на уровне потоков, ожидая завершения ассинхронного запроса и возвращая результат после этого. По скорости язык хорош, единственное что плохо в нём — работа с ядрами процессора, всё выполняется на одном. Но это, я надеюсь, однажды решит гугл во всем V8.
>>всё выполняется на одном. Но это, я надеюсь, однажды решит гугл во всем V8.
Так же задумано by design.

Проблема решается например воркерами
Сейчас гугл будет толкать свою хром-ос. Вместе с ней будут продвигаться игры и прочее на чистом хтмл5. И тут будет грешно не давать использовать весь потенциал процессора в пассивном режиме.
Использовать хоть 2, хоть 10 ядер процессора можно уже сейчас. Воркеры есть. Если сможете расспаралелить своё приложение.
Но от однопоточной модели Javascript все равно никуда не денешься.
Однопоточная модель не является ограничением JavaScript, а скорее является ограничением его реализации в виде V8. Большая часть кода V8 не является потоко-безопасной.

Хотя были форки с реализацией многопоточности, но они полудохлые, так как пока не появилось особой необходимости в них, в отличие от волокон.
Язык JavaScript (ECMAScript) не содержит примитивов синхронизации и не описывает семантику паралельного исполнения. Сравните, например, с Java — в Java Language Specification целая нетривиальная глава посвящена потокам и их семантике: java.sun.com/docs/books/jls/third_edition/html/memory.html

Единственное, что безопасно можно уложить в JavaScript — это share nothing многозадачность, которая в рамках V8 API легко реализуется через изоляты.
Лет двадцать, если не больше, спецификация языка C++ не содержала примитивов синхронизации и не описала семантику параллельного выполнения. Вы бы ещё с C# сравнили.
Что не мешало создавать многопоточные приложения. Вообще, введение примитивов многопоточности, — это весьма недавний тренд.
в Algol 68 были par и sem ;-)

Я уж не говорю про специализированные языки типа occam.

Я не отрицаю, можно и без них, но c ними удобнее и безопаснее.
Согласен, был чересчур категоричен :) Незнаком просто с Алголом.
Я бы не стал сравнивать C++ с JavaScript.

С++ близок к железу и стандарт явным образом оставляет многое на усмотрению разработчику компилятора. У меня не под рукой старого стандарта, но новый явным образом описывает модель исполнения как nondeterministic abstract machine.

Ничего подобного в ECMA-262 нет. Есть только заданные на уровне синтаксического дерева правила вычислений. Как их совместить с недетерминизмом параллельного исполнения не совсем ясно.

Учитывая что JavaScript подразумевает достаточно толстый runtime и нетривиальную работу с объектами, легко представить ситуацию когда две программы с потоками будут давать совершенно разные результаты на двух разных реализациях или трудно диагнастируемые баги (вспомним как канонический пример проблемы с double checked locking до появления внятной memory model в Java). Или будет страдать производительность.

Да и без примитивов синхронизации писать код c синхронизациями действительно будет тяжко. В Java на уровне языка есть synchronized методы и блоки + volatile, на уровне библиотеки java.util.concurrent, в C++ (если мы смотрим на C++ до C++11) есть хотя бы деструкторы стековых объектов => паттерн RAII.

В JS будь там многопоточность без поддержки на примитивов синхронизации на синтаксическом уровне пришлось бы делать:
this.mutex.lock();
try {
  this.f(this.x++);
} finaly {
  this.mutex.unlock();
}


волосато…

конечно можно оформить хелпер

function sync(receiver, mutex, f) {
  mutex.lock();
  try {
    f.call(receiver);
  } finaly {
    mutex.unlock();
  }
}

sync(this, this.mutex, function () {
  this.f(this.x++);
});


но все равно мало радости.
У нас, наверное, разный бэкграунд. Я в своё время много писал и на C++, меня отсутствие примитивов не смущало, хотя большая часть приложений была многопоточной (Win32 API+ATL).

Хотя, конечно, оператор lock в C# изрядно порадовал.

А если считать за основу стандарт C++11, то, считай, больше тридцати лет в языке примитивов синхронизации не было.

Появятся, конечно, в связи с выпуском операционной системы Chrome OS.
Да я не отрицаю, можно и без примитивов. Хотя в том же MFC был CMutex и прочие радости, чтобы минимизировать вероятность прострела ноги при жонглировании с HANDLEами и WaitFor*, ReleaseMutex.

Я тут бы даже примитивы на первое место не ставил, а ставил то, что нет ни формальной семантики, ни хотя бы упоминания чего нибудь связанного с параллелизмом и недетерминизмом в стандарте ECMA-262.
MFC — вообще ни разу не стандарт.

С таким же успехом можно упомянуть класс Locker, включенный в V8.
> MFC — вообще ни разу не стандарт.

А я что сказал, что стандарт? Я CLocker к тому упомянул, что люди нуждаются в RAII-based велосипедах, которые бы для них управляли локами, чтобы не прострелить себе ногу ручным управлением. А вот если стандарт включает либо примитивы, либо хотябы библиотечку официальных «оберток», то потребность в велосипедах отпадает. Переносимость повышается. Карма очищается и чакры открываются ;-)
тьфу, CMutex, совсем уже крыша поехала ;-)
Ещё раз, — подобные классы есть в V8 (JavaScript VM). Просто ими редко пользуются.
Тут я потерял нить разговора если честно :-)

Locker в V8 — это несколько большее, чем обертка на простым мьютексом. Когда человек пишет на С++ он может синхронизировать или не синхронизировать потоки, как его душе угодно. Но в V8 как только ему захотелось использовать V8 из двух разных потоков — он обязан использовать Locker, для того чтобы a) гарантировать эксклюзивный доступ к VM b) гарантировать, что VM правильно инициализует внутренние структуры данных соответствующие потоку.

Если бы даже в C++ были примитивы синхронизации, Locker ими не заменить, он сложнее.
Согласен. А по-другому, увы, пока никак.

Сейчас многопоточную обработку делают на C++, как вручную, так и с помощью libeio.

А JS используют в качестве клея.
Ну и Locker (как, например, и другая фича V8 — preemption) — это скорее деталь реализации. Нестандартная плюшка, никаким образом не торчащая через pure js api.
до C++11 единственной стандартной плюшкой было ключевое слово volatile. всё остальное торчало тридцать лет с гаком через нестандартные библиотеки.
> Однопоточная модель не является ограничением JavaScript, а скорее является ограничением его реализации в виде V8

V8 как таковой тут непричем, насколько я знаю все реализации JavaScript однопоточные. Различные штуки в виде WebWorkers позволяют использовать потоки, однако разделения на уровне переменных не происходит, так что это больше похоже по смыслу на отдельные процессы. И это кстати хорошо.
Достаточно раскидывать коллбэки на менее загруженные ядра для начала.
Это не является ограничением языка. Более того, на сегодня в Node.JS возможна и многопоточная обработка данных (Web Workers), и событийная машина на нескольких ядрах (передача FD дочернему процессу, multinode, cluster), и псевдосинхронная обработка (node-fibers).
Эта статья, также как и мое интервью с @ryah, годовалой давности, хотя критика асинхронности актуальна и сегодня.

Большинство приложений можно и нужно писать в синхронном стиле. На Ноде это можно сделать с fibers и моей библиотекой Common Node.

Ссылка на бенчмарки в README.
Мне понравилась эта статья именно своей корректностью (серебряной пули нет, снова и снова). Поэтому и перевёл.
Большое спасибо за перевод, я полностью согласен. Просто хотел сказать что дата опять не указана в начале статьи.
Смысла особого нет (все переводы пишу «как есть»). Всё до сих актуально.
UFO just landed and posted this here
добавлю, — и полезно.

слишком часто Node воспринимают как панацею.
UFO just landed and posted this here
Есть такое ощущение. Я, впрочем, до сих пор не понял, чего конкретно Ryan пытается достичь с node.
Помимо недавнего тренда с Windows/libux, я пока заметил только стремление сделать удобное окружение для создания сервисов, похожих на те, которые создавал раньше он на Си.

Фактически новые API ветки 0.4 намного удобнее прежних (0.2).

На самом деле я даже не вижу смысла развивать Node особо, скорее дополнять его библиотеку модулей, и предоставить несколько воркеров, файберы и yield из коробки. Ну и ещё реализовать горячую перезагрузку кода тоже из коробки.

Больше ничего особо не надо.
UFO just landed and posted this here
UFO just landed and posted this here
В этом я с вами согласен, по большей части моя работа с Node.js именно ради процесса идёт.
Я кстати вообще не понимаю как работает асинхронность в js… Может кто-нибудь объяснить?
Как везде. Операционная система предоставляет API для генерации событий и для ожидания оных.

При этом, пока процесс ждёт возникновения событий, он не тратит времени CPU, а просто спит. В нужный момент ОС его разбудит.

Помимо событий ввода/вывода, типичные события — срабатывание таймера, таймаут или завершение другого процесса.

Кстати, часть асинхронности обеспечивается фоновым запуском (незаметным разработчику) отдельных потоков выполнения, если иначе обеспечить асинхронность нельзя. Типичный пример — node-mysqlclient.
Пожалуй тут важно добавить что система может ждать одновременно наступления множества событий. Вы установили обработчики и вернули управление. Пока ваш скрипт ждет вызова обработчиков, система может выполнять другие скрипты. А события наступают асинхронно — вы не знаете какой обработчик когда вызовется.
Есть более прямолинейный вариант с фибрами — когда выполнение скрипта приостанавливается в некоторой точке до наступления события, а потом возобновляется с этой же точки.

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

Потоки (вымещающая многозадачность) не имеет этого недостатка, потому что там передача управления может произойти не только во время ожидания завершения операции, но и в любой другой момент. Вы не знаете в какой момент ваш код будет преостановлен и выполнен чужой код. Проблема в том что создание/удаление потоков — дорогая операция. Часто создавать поток выйдет дороже, чем сама операция ввода-вывода. Плюс бывают нужны блокировки, о которых тоже нельзя забывать. Раньше для решения проблемы использовали пулы заранее созданных потоков.

Сейчас чаще совмещают кооперативную и вымещающую многозадачность. Наиболее красиво эта идея, да и вообще масштабирование, на мой взгляд сделано в Erlang. Очень рекоммендую…
Sign up to leave a comment.

Articles