Откуда ноль ? Многозадачное в однозадачном
30.04.2016
|
Pauel |
Есть некоторый кусочек кода(ниже). Это олдскульный node, фактически — тот самый однопоточный JavaScript.
Итого — пусть N читателей-писателей работают с глобальным ресурсом по протоколу read-write. Для простоты, глобальный ресурс есть просто файл. Каждый читатель считывает его целиком. Каждый писатель перезаписывает целиком.
Цикл работает примерно так
— читаем контент из файла ('i')
— конвертируем его число
— инкрементим число на 1
— записываем число в тот же файл ('i')
— пишем в консоль промежуточные результаты
Если был запущен один экземпляр функции, вывод получается примерно таким,
Рабочий код, node.js:
Если файл изначально не существовал, то будет вывод примерно такого вида:
Предположим, файл существует и хранит, скажем, значение 100. Через короткое время после начала работы вывод снова становится вот таким:
Вопрос — если файл изначально хранит, скажем, 100, откуда берется 0 ?
Пример демонстрирует тёмную сторону node.js и JavaScript в частности. Фактически, речь про кооперативную многозадачность, только с ручным приводом. А раз это многозадачность, то в ней возможны все проблемы свойственные многозадачности вообще. Одна из таких проблем — гонки или race condition. Парадокс, почти на всех сайтах, особенно Микрософта, говорится о том, что никаких race conditions в JavaScript нет и быть не может.
Собственно, авторы таких строк абсолютно правы. Только они забыли сказать, что имели ввиду потоковую многозадачность, которой в JavaScript действительно нет — JavaScript работает в одном физическом потоке.
Тут надо вспомнить, что основных моделей паралеллизма(concurrency) две штуки — потоковая и событийная. Поток в JavaScript один. А вот событийную модель никто не отменял. А она, коварная, как раз и даёт ту самую кооперативную многозадачность со всеми достоинствами и недостатками.
Итого — пусть N читателей-писателей работают с глобальным ресурсом по протоколу read-write. Для простоты, глобальный ресурс есть просто файл. Каждый читатель считывает его целиком. Каждый писатель перезаписывает целиком.
Цикл работает примерно так
— читаем контент из файла ('i')
— конвертируем его число
— инкрементим число на 1
— записываем число в тот же файл ('i')
— пишем в консоль промежуточные результаты
Если был запущен один экземпляр функции, вывод получается примерно таким,
after read 0
after write 1
after read 1
after write 2
after read 2
after write 3
after read 3
after write 4
after read 4
Рабочий код, node.js:
// коду примерно три года, некоторые особенности могут проявляться по разному в разных версиях node.
var fs = require('fs');
function read_i(clb) {
fs.readFile('i', function(e, i) { clb(toNumber(i)); });
}
function write_i(i, clb) {
fs.writeFile('i', i, clb);
}
function toNumber(buffer) {
return +(buffer || 0).toString();
}
function task() {
read_i(function(i) {
console.log('after read', i);
write_i(i + 1, function() {
console.log('after write', toNumber(fs.readFileSync('i'))); // debug
task();
});
});
}
task();
task();
task();
task();
task();
task();
Если файл изначально не существовал, то будет вывод примерно такого вида:
after read 0
after read 0
after write 0
after read 5
after read 0
Предположим, файл существует и хранит, скажем, значение 100. Через короткое время после начала работы вывод снова становится вот таким:
after read 0
after read 0
after write 0
after read 5
after read 0
Вопрос — если файл изначально хранит, скажем, 100, откуда берется 0 ?
Пример демонстрирует тёмную сторону node.js и JavaScript в частности. Фактически, речь про кооперативную многозадачность, только с ручным приводом. А раз это многозадачность, то в ней возможны все проблемы свойственные многозадачности вообще. Одна из таких проблем — гонки или race condition. Парадокс, почти на всех сайтах, особенно Микрософта, говорится о том, что никаких race conditions в JavaScript нет и быть не может.
Собственно, авторы таких строк абсолютно правы. Только они забыли сказать, что имели ввиду потоковую многозадачность, которой в JavaScript действительно нет — JavaScript работает в одном физическом потоке.
Тут надо вспомнить, что основных моделей паралеллизма(concurrency) две штуки — потоковая и событийная. Поток в JavaScript один. А вот событийную модель никто не отменял. А она, коварная, как раз и даёт ту самую кооперативную многозадачность со всеми достоинствами и недостатками.
30.04.2016 0 комментариев |