понедельник, 28 декабря 2009 г.

Open, каналы и ожидание завершения потомка

Трудно искать черную кошку в темной комнате, особенно, если ее там нет.
Но как оказалось, еще трудней не замечать черную кошку, сидящую на видном месте в ярко освещенной комнате!

Так вот и я потратил приличную часть времени, заметив, что в коде, упрошенном до нижеследующего, родительский процесс ожидает завершения потомка:

my $child_sub = sub { sleep };

my $child_pid = open my $fh, "-|";
defined $child_pid or die "Can't fork: $!";

if ($child_pid) {
# Родитель.
} else {
# Ребенок.
$child_sub->();
exit;
}

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

my $child_sub = sub { sleep };

pipe my $from_child_fh, my $to_parent_fh or die "pipe: $!";

my $child_pid = fork;
defined $child_pid or die "Can't fork: $!";

if ($child_pid) {
# Родитель.
close $to_parent_fh;
} else {
# Ребенок.
close $from_child_fh;
$child_sub->();
exit;
}

Или буду использовать socketpair.

С другой стороны, вред от первого варианта на практике всегда отсутствует, наверно поэтому и не замечал этого явления на протяжении многих лет.

понедельник, 21 декабря 2009 г.

Настоящие джедаи не используют threads

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

Еще на заре истории старые мастера предупреждали, что потоки, разделяемые данные, блокировки и семафоры - это не хорошо, излишне и путано. Достаточно процессов и мультиплексирования. Но не послушали их, и были созданы пользовательские потоки, внеся в этот мир сумятицу и сложность. Да и как создали? Эх...

В Linux пошли по самому легкому пути: по сути потоки являются немного облегченными процессами. Горячие головы пытались исправить это, но увидели мытарства разработчиков Solaris остудили свой пыл. И только в Windows-NT один из старых мастеров-ренегатов создал нормальные потоки. Но это была медвежья услуга - мир захотел потоков еще больше.

Даже в лагере BSD начались волнения. Потоки во FreeBSD были, но о них ядро ничего не знало со всеми вытекающими последствиями. Поэтому в основном использовали реализацию Linux потоков. Но лучшая реализация Windows потоков не давала покоя и разработчики решили, что смогут сделать то, что не удалось сделать в Solaris. Так появилась FreeBSD 5 с KSE. Потом выпустили FreeBSD 6, затем 7 и вот недавно - 8, в которой от KSE не осталось ни следа, а потоки во FreeBSD стали иметь модель как у Windows и Solaris: 1 к 1.

Но не все так мрачно. Еще по времена FreeBSD 4, один из джедаев, помня советы старых мастеров, сделал форк FreeBSD и назвал его DragonFlyBSD. Одной из целей этого проекта стала очистка от блокировок и всего, что мешает масштабированию системы. Вместо блокировок - сообщения.

И в мире повыше, чем системное программирование, есть островки надежды. Например Erlang, с концепцией легковесных процессов, общающихся посредством сообщений. Также интересен Mozart-OZ. Да и функциональные языки хорошо распараллеливаются. Эти подходы позволяют скрыть от простого программиста "сложность" мультиплексирования и межпроцессорного взаимодействия не внося при этом недостатки, свойственные потокам. А настоящие джедаи понимают, что эта сложность лишь иллюзия.

Что касается Perl, то он у меня до сих пор собран без поддержки потоков.
Ой, как в этой заметки оказался Perl? Просто эта заметка является исповедью: мне пришлось написать многопоточное приложение на Perl.
Но перед советом джедаев у меня есть два оправдания: мультиплексирующая и многопроцессная версии этого приложения!

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

Все таки был прав батюшка из фильма "Королева бензоколонки", сказав: "к полумерам не привык", либо процессы, либо мультиплексирование (ситуацию с Windows не рассматриваем).

P.S.
Перечитал - сплошной вихрь эмоций, а не заметка.

P.P.S.
Радует то, что разработчика Perl уже давно трезво смотрят на потоки. Для эмуляции системного вызова fork под Windows и для использования mod_perl2 с Apache2 в многопоточном режиме они реализовали многопоточность на основе копий интерпретатора Perl, а предыдущей модели Perl потоков забыли как о страшном сне.

Разработчики же других динамических языков все еще находятся в эйфории от потоков, благо их языки намного проще. Например, вчера случайно попал на совещание питоноводов, на котором обсуждались вопрос частого падения их проекта при высокой нагрузке, а также вопрос, почему часто вместо ожидаемых данных возвращается другая информация, как будто от другого запроса. Услышав это, зная как реализованы потоки в питоне, порекомендовал использовать Apache2 в многопроцессном режиме работы. Система стала работать стабильно и корректно.

понедельник, 14 декабря 2009 г.

Почему я не использую ORM

Для Perl на CPAN существует большое количество различным ORM, да и очень часто на конференциях делают доклады о них.
Поэтому меня время от времени возникала мысль написать cтатью "Почему я не использую ORM".

Но потом я увидел одну фразу, и смыл в данной статье отпал, так как в этой одной простой фразе содержался исчерпывающий ответ.
Вот эта фраза, сказанная http://plumqqz.livejournal.com:
"Высокоуровневый декларативный язык (SQL) в случае ORM заменяется низкоуровневым процедурным..."

От себя вкратце добавлю, что не использую в основном потому что:
1. Движки баз слишком разные. И от базы зависит не только как выбрать информацию с таблиц, но сама структура таблиц и индексов.
2. ORM - это лишний промежуточный слой...

Но, я не всегда пишу чистый SQL. Иногда генерирую его с более высокоуровневого MetaSQL, который учитывает специфику конкретного проекта. Да и MetaSQL иногда бывает частью некого декларативного описания более высокого уровня и генерируется на его основе, совместно с генерацией части Perl кода.

понедельник, 7 декабря 2009 г.

Обновил модуль BGS

Обновил модуль BGS - Background execution of subroutines in child processes.
Вместо open используется pipe - теперь дочерний процесс может печатать на STDOUT.
Также сделал, что bgs_call возвращает PID дочернего процесса. Зачем это сделал - не знаю,
наверно, надо было бы лучше добавить проверку на допустимость возвращаемого значения блока bgs_call.