LINUX.ORG.RU

[perl] Потоки - базовые вопросы (Coro и остальные)

 


0

0

Имеется два вопроса:

1. Coro это псевдо-потоки (может быть fibers) ?. У меня pstree -p pid показывает только один процесс. Можете ли показать правильный вариант для одновременного запуска нескольких потоков, скажем на основании массива, что-то вроде while (@arr) { my $d = shift @arr; async_pool { func1($d) }; }

2. Есть ли какой-нибудь модуль позволяющий запускать независимые параллельные потоки, желательно с pthread_setaffinity ?

P.S. Env: Linux + perl 5.12 + Coro latest version

★★★★★

Последнее исправление: gh0stwizard (всего исправлений: 1)

aur/perl-coro 5.372-1 (0) the only real threads in perl

это оно? зачем это надо?

top -H что показывает?

Кстати в линуксе впринципе потоков нет, это всё процессы.

DELIRIUM ☆☆☆☆☆
()

Coro - это модуль для создания сопрограмм (coroutines). Это не threads в привычном их понимании. Он используется в основном когда нужно «распараллелить» операции ввода/вывода (мультиплексирование), но при этом не хочется переходить на событийно-ориентированный стиль написания кода, который предлагают модули вроде POE и AnyEvent. Соответственно все происходит в рамках одного процесса и потока и со стороны выглядит как использование потоков.

Для настоящих потоков используется модуль threads, но у него свои минусы.

Olegymous ★★★
()
Ответ на: комментарий от DELIRIUM

Какая разница через что их можно сделать? Может ты почитаешь про отличие потоков от процессов хотя бы на википедии?

Reset ★★★★★
()
Ответ на: комментарий от Olegymous

Спасибо, а то я все прочитал и все понял, но Марк так расписал будто у него реальные потоки (трудности перевода :) Насчет threads:

1. Без сборки перла с --enable-ithreads можно спокойно использовать этот модуль?

2. На выходе получаем тоже самое, что при использовании clone(2) ? Про CLONE/CLONE_SKIP прочитал.

Буду разбираться, просто не понятен смысл тогда Coro, если основное преимущество теряется при use threads: возможность «рассеивания» по ядрам/процессорам. Для меня важен именно последний пункт, так называемый Work Crew

Можете привести пример где Coro действительно нужен? Для параллельных вычислений он не подходит, т.к. процессор/ядро одно и тот же while (@arr) { count++; } будет на микродолю быстрее. Разве что для GUI программ: например отобразить иконки для файлов: быстро заспавнить весь список файлов и последовательно запускать поиск иконки, тогда их отображение будет немного хаотичным и при этом основное приложение будет почти всегда доступно (т.к. будет возрат управления через cede).

gh0stwizard ★★★★★
() автор топика
Ответ на: комментарий от gh0stwizard

1. Без сборки перла с --enable-ithreads можно спокойно использовать этот модуль?

Насколько понимаю, нельзя.

2. На выходе получаем тоже самое, что при использовании clone(2) ? Про CLONE/CLONE_SKIP прочитал.

Ну там обычные pthreads. Только при создании нового потока в нём создается новая копия интерпретатора, а данные между потоками не шарятся, как это должно быть в нормальных потоках. Данные можно расшарить если использовать модуль threads::shared, но шарятся они не по человечески, а путём копирования. Таким образом если в одном потоке у нас переменная на 10 мб и мы ее расшарим и заюзаем во втором потоке, то в нем создастся её копия и наша программа уже потребляет 20 мб. При изменении переменных копии незаметно для программиста синхронизируются, как будто бы это одна и та же переменная. Зато в отличии от языков python и ruby в потоках perl отсутствует GIL и реально используются все доступные процессоры. Если дочитали документацию Coro до конца то могли видеть что Марк думает о реализации потоков в Perl (раздел «WINDOWS PROCESS EMULATION»)

Можете привести пример где Coro действительно нужен?

Уже обсуждалось, например тут
Еще недавно была конфереyция YAPC::Asia, где Марк рассказывал в том числе для чего он делал Coro: http://www.youtube.com/watch?v=Oe5fCzQtVsg

Olegymous ★★★
()
Ответ на: комментарий от Olegymous

Никогда бы не подумал, что анимешник на такое способен :) Видео дало ответы на все вопросы, спасибо еще раз.

gh0stwizard ★★★★★
() автор топика

Может быть стоит попробовать ps -Lp pid? -L показывает потоки.

iliyap ★★★★★
()
Ответ на: комментарий от Olegymous

Шаренные переменные в perl это непростая штука. Когда ты делаешь share($scalar) (или share(%hash)), значение переменной теряется. А почему? Потому что модуль threads::shared на самом деле создаёт ещё один (скрытый) глобальный интерпретатор, и создаёт шаренные переменные в этом глобальном интерпретаторе. В остальных (рабочих) интерпретаторах шаренные переменные содержат ссылки на шаренные переменные из этого глобального интерпретатора. С каждой шаренной переменной в глобальном интерпретаторе связан мьютекс и кондвар. Когда вы в одном из рабочих интерпретаторов выполняете lock(), cond_wait(), или cond_signal() на шаренной переменной, используются связанные с этой шаренной переменной мьютекс и кондвар.

Вобщем никакой репликации данных не происходит. Данные действительно шарятся между интерпретаторами. Просто для этого используется дополнительный уровень индирекции.

iliyap ★★★★★
()
Ответ на: комментарий от iliyap

Как в таком случае объяснить рост памяти с увеличением числа потоков (переменная $i) в следующей программе?

use threads;
use threads::shared;
use strict;

my $var = "xxx" x 1_000_000;
share($var);

my $i = 10;
for (1..$i) {
        async {
                {
                        lock($var);
                        $var .= "y";
                }
                warn "thr $_ sleeping";
                sleep 100;
        }
}

warn "Main thread waiting";
<>;

16 мб при $i=1 и 70 мб при $i=10. Больше похоже на копию, чем на ссылку.

Olegymous ★★★
()
Ответ на: комментарий от Olegymous

При создании нового perl-потока (а блок async {} создаёт новый perl-поток) во-первых создаётся новый поток со своим стеком (по умолчанию в Linux это 10 MB адресного пространства), а во-вторых создаётся копия интерпретатора, исполняющаяся в этом потоке.

Проверить, что шаренные переменные существуют действительно в одном экземпляре, можно с помощью sysread(). Если делать sysread() в шаренную переменную из разных perl-потоков, strace покажет, что системному вызову read() передаётся один и тот же адрес буфера.

iliyap ★★★★★
()
Ответ на: комментарий от iliyap

Всё хорошо, но тот же код с пустой переменной $var даёт 7 мб на программу, против 70-ти с наполненной. Значит копия интерпретатора создается все таки с копией нашей большой шаренной переменной?

Olegymous ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.