среда, 3 февраля 2010 г.

Всякая всячина и книги

Пора заканчивать заниматься ерундой и писать всякую фигню o Perl - блог закрыт. Сажусь за написание двух книг.

Первая книга будет называется "Событийное программирование на Perl для домохозяек или как успеть приготовить тысячу блюд к приходу гостей".

Вторая - "Распределенное программирование на Perl для домохозяек".
Эта книга предназначена для тех, кто любому кухонному комбайну предпочитает набор хороших кухонных ножей.

Логичней было начать с первой, но вторая сейчас интересней, поэтому есть большая вероятность, что первая будет лишь брошюркой.

Здесь будет опубликованы анонсы. До встречи.

понедельник, 4 января 2010 г.

Если бы в Perl не было бы списков :-)

Новогодние каникулы продолжаются...
Иногда в праздничном веселье, как нечто далекое, мелькают мысли: кто я, что я делал, perl какой-то...

Создание списка

sub list($;$) {
my ($h, $t) = @_;
return sub {
return $h, $t;
}
}

my $x1 = list(1);
my $x2 = list(2, $x1);
my $x3 = list(3, $x2);

Печать списка

sub print_list($);
sub print_list($) {
my ($list) = @_;
my ($h, $t) = $list->();
print_list($t) if $t;
print "$h ";
}

print_list $x3;
print "\n";

map

sub _Map {
my ($sub, $list) = @_;
my ($h, $t) = $list->();
my $r = $t ? _Map($sub, $t) : undef;
list($sub->($h), $r);
}
sub Map(&;$) { &_Map }

print_list Map { $_[0]**$_[0] } $x3;
print "\n";

понедельник, 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.

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

do, eval и return

Написал

my (@foo) = do {
...
return ...
};

держа в голове

my (@foo) = eval {
...
return ...
};

А не надо было в варианте с do использовать return, так как это выход не из do, а из подпрограммы, содержащей do.

:-)