threads
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- ПРЕДУПРЕЖДЕНИЕ
- СИНОПСИС
- ОПИСАНИЕ
- ЗАВЕРШЕНИЕ ПОТОКА
- СОСТОЯНИЕ ПОТОКА
- КОНТЕКСТ ПОТОКА
- РАЗМЕР СТЕКА ПОТОКА
- СИГНАЛИЗАЦИЯ ПОТОКА
- ПРЕДУПРЕЖДЕНИЯ
- ОШИБКИ
- ОШИБКИ И ОГРАНИЧЕНИЯ
- ТРЕБОВАНИЯ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- ЛИЦЕНЗИЯ
- БЛАГОДАРНОСТИ
НАЗВАНИЕ
threads - Потоки Perl, основанные на интерпретаторе
ВЕРСИЯ
В данном документе описываются потоки версии 2.36
ПРЕДУПРЕЖДЕНИЕ
«Потоки, основанные на интерпретаторе», предоставляемые Perl, не являются быстрой и легкой системой для многозадачности, на которую можно было бы рассчитывать. Реализация потоков спроектирована так, что их легко использовать неправильно. Немногие люди знают, как правильно их использовать, или смогут оказать помощь.
Использование потоков Perl интерпретатора официально не рекомендуется.
СИНОПСИС
use threads ('yield',
'stack_size' => 64*4096,
'exit' => 'threads_only',
'stringify');
sub start_thread {
my @args = @_;
print('Thread started: ', join(' ', @args), "\n");
}
my $thr = threads->create('start_thread', 'argument');
$thr->join();
threads->create(sub { print("I am a thread\n"); })->join();
my $thr2 = async { foreach (@files) { ... } };
$thr2->join();
if (my $err = $thr2->error()) {
warn("Thread error: $err\n");
}
# Invoke thread in list context (implicit) so it can return a list
my ($thr) = threads->create(sub { return (qw/a b c/); });
# or specify list context explicitly
my $thr = threads->create({'context' => 'list'},
sub { return (qw/a b c/); });
my @results = $thr->join();
$thr->detach();
# Get a thread's object
$thr = threads->self();
$thr = threads->object($tid);
# Get a thread's ID
$tid = threads->tid();
$tid = $thr->tid();
$tid = "$thr";
# Give other threads a chance to run
threads->yield();
yield();
# Lists of non-detached threads
my @threads = threads->list();
my $thread_count = threads->list();
my @running = threads->list(threads::running);
my @joinable = threads->list(threads::joinable);
# Test thread objects
if ($thr1 == $thr2) {
...
}
# Manage thread stack size
$stack_size = threads->get_stack_size();
$old_size = threads->set_stack_size(32*4096);
# Create a thread with a specific context and stack size
my $thr = threads->create({ 'context' => 'list',
'stack_size' => 32*4096,
'exit' => 'thread_only' },
\&foo);
# Get thread's context
my $wantarray = $thr->wantarray();
# Check thread's state
if ($thr->is_running()) {
sleep(1);
}
if ($thr->is_joinable()) {
$thr->join();
}
# Send a signal to a thread
$thr->kill('SIGUSR1');
# Exit a thread
threads->exit(); ОПИСАНИЕ
Начиная с Perl 5.8, программирование потоков стало доступным с помощью модели интерпретируемых потоков, которая предоставляет новый интерпретатор Perl для каждого потока и, по умолчанию, не приводит к совместному использованию данных или состояния между потоками.
(До Perl 5.8 был доступен 5005threads через Thread.pm API. Эта модель потоков устарела и была удалена начиная с Perl 5.10.0.)
Как только что было сказано, все переменные по умолчанию являются локальными для каждого потока. Для использования общих переменных необходимо также загрузить threads::shared:
use threads;
use threads::shared; При загрузке threads::shared, необходимо use threads перед тем, как use threads::shared. (threads выведет предупреждение, если это сделано в обратном порядке.)
Сильно рекомендуется включить потоки с помощью use threads как можно раньше в вашем скрипте.
При необходимости скрипты могут быть написаны так, чтобы работать как с потоковыми, так и с непотоковыми Perl:
my $can_use_threads = eval 'use threads; 1';
if ($can_use_threads) {
# Do processing using threads
...
} else {
# Do it without using threads
...
} - $thr = threads->create(FUNCTION, ARGS)
-
Это создаст новый поток, который начнет выполнение с указанной функции входа и передаст ему список ARGS в качестве параметров. Он вернет соответствующий объект потока или
undefв случае неудачи при создании потока.FUNCTION может быть именем функции, анонимной подпрограммой или ссылкой на код.
my $thr = threads->create('func_name', ...); # or my $thr = threads->create(sub { ... }, ...); # or my $thr = threads->create(\&func, ...);Метод
->new()является псевдонимом для->create(). - $thr->join()
-
Это ожидает завершения выполнения соответствующего потока. По окончании потока
->join()вернет значение(я) возвращаемой функции входа.Контекст (пустой, скалярный или списочный) для возвращаемого значения(й) для
->join()определяется на этапе создания потока.# Create thread in list context (implicit) my ($thr1) = threads->create(sub { my @results = qw(a b c); return (@results); }); # or (explicit) my $thr1 = threads->create({'context' => 'list'}, sub { my @results = qw(a b c); return (@results); }); # Retrieve list results from thread my @res1 = $thr1->join(); # Create thread in scalar context (implicit) my $thr2 = threads->create(sub { my $result = 42; return ($result); }); # Retrieve scalar result from thread my $res2 = $thr2->join(); # Create a thread in void context (explicit) my $thr3 = threads->create({'void' => 1}, sub { print("Hello, world\n"); }); # Join the thread in void context (i.e., no return value) $thr3->join();См. "КОНТЕКСТ ПОТОКА" для получения более подробной информации.
Если программа завершается, не все потоки были соединены или отделены, то будет выведено предупреждение.
Вызов
->join()или->detach()для уже соединенного потока приведет к ошибке. - $thr->detach()
-
Делает поток несоединяемым и вызывает отбрасывание любого возвращаемого значения. При завершении программы любые отделенные потоки, которые все еще выполняются, будут безмолвно завершены.
Если программа завершается, не все потоки были соединены или отделены, то будет выведено предупреждение.
Вызов
->join()или->detach()для уже отделенного потока приведет к ошибке. - threads->detach()
-
Метод класса, позволяющий потоку отсоединиться.
- threads->self()
-
Метод класса, позволяющий потоку получить свой собственный объект threads.
- $thr->tid()
-
Возвращает идентификатор потока. Идентификаторы потоков — это уникальные целые числа, основной поток в программе имеет значение 0, а последующие потоки инкрементируются на 1.
- threads->tid()
-
Метод класса, позволяющий потоку получить свой собственный идентификатор.
- "$thr"
-
Если вы добавите опцию
stringifyв объявлениеuse threads, то использование объекта потока в строке или контексте строки (например, в качестве ключа хэша) приведет к использованию его идентификатора в качестве значения:use threads qw(stringify); my $thr = threads->create(...); print("Thread $thr started\n"); # Prints: Thread 1 started - threads->object($tid)
-
Это вернет объект threads для активного потока, связанного с указанным идентификатором потока. Если
$tidявляется значением для текущего потока, то этот вызов работает так же, как->self(). В противном случае, возвращаетundefесли нет потока, связанного с TID, если поток соединён или отделён, если TID не указан или если указанный TID неопределён. - threads->yield()
-
Это предложение операционной системе позволить данному потоку уступить время процессора другим потокам. Что фактически произойдёт, сильно зависит от реализации потоков на нижнем уровне.
Вы можете выполнить
use threads qw(yield), а затем просто использоватьyield()в своём коде. - threads->list()
- threads->list(threads::all)
- threads->list(threads::running)
- threads->list(threads::joinable)
-
Без аргументов (или с использованием
threads::all) и в контексте списка, возвращает список всех объектов threads, которые не были объединены и не были отделены. В скалярном контексте возвращает количество таких объектов.С аргументом true (с использованием
threads::running) возвращает список всех объектов threads, которые не были объединены и не были отделены и которые все еще выполняются.С аргументом false (с использованием
threads::joinable) возвращает список всех объектов threads, которые не были объединены и не были отделены и которые завершили выполнение (т.е., для которых->join()не будет блокироваться). - $thr1->equal($thr2)
-
Проверяет, являются ли два объекта потоков одним и тем же потоком. Это перегружено для более естественных форм:
if ($thr1 == $thr2) { print("Threads are the same\n"); } # or if ($thr1 != $thr2) { print("Threads differ\n"); }(Сравнение потоков основано на идентификаторах потоков.)
- async BLOCK;
-
asyncсоздаёт поток для немедленного выполнения блока кода, следующего за ним. Этот блок обрабатывается как анонимная подпрограмма, и поэтому после закрывающей фигурной скобки должен стоять знак точки с запятой. Как иthreads->create(),asyncвозвращает объект threads. - $thr->error()
-
Потоки выполняются в контексте
eval. Этот метод вернётundefесли поток завершился нормально. В противном случае, он возвращает значение$@связанное со статусом выполнения потока в его контекстеeval. - $thr->_handle()
-
Этот приватный метод возвращает указатель (т.е., адрес памяти, представленный как целое без знака) на внутреннюю структуру потока, связанную с объектом потока. Для Win32 это указатель на значение
HANDLEвозвращённоеCreateThread(т.е.,HANDLE *); для других платформ — это указатель на структуруpthread_tиспользуемую в вызовеpthread_create(т.е.,pthread_t *).Этот метод не нужен для общего программирования потоков Perl. Его цель — предоставить другим (модулям на основе XS) модулям потоков возможность доступа и, возможно, манипулирования внутренней структурой потока, связанной с потоком Perl.
- threads->_handle()
-
Метод класса, позволяющий потоку получить свой собственный дескриптор.
ЗАВЕРШЕНИЕ ПОТОКА
Обычный способ завершения потока — это return() из функции входа с соответствующим возвращаемым значением(ями).
- threads->exit()
-
Если нужно, поток можно завершить в любой момент, вызвав
threads->exit(). Это заставит поток вернутьundefв скалярном контексте или пустой список в списочном контексте.Когда вызывается из главного потока, это поведение аналогично
exit(0). - threads->exit(status)
-
При вызове из потока это поведение аналогично
threads->exit()(т.е., код состояния выхода игнорируется).Когда вызывается из главного потока, это поведение аналогично
exit(status). - die()
-
Вызов
die()в потоке указывает на аномальный выход из потока. Любой обработчик$SIG{__DIE__}в потоке будет вызван в первую очередь, а затем поток выйдет с предупреждающим сообщением, которое будет содержать любые аргументы, переданные в вызовdie(). - exit(status)
-
Вызов exit() внутри потока приводит к завершению всей программы. По этой причине использование
exit()внутри многопоточного кода или в модулях, которые могут использоваться в многопоточных приложениях, настоятельно не рекомендуется.Если
exit()действительно необходимо, то рассмотрите следующий вариант:threads->exit() if threads->can('exit'); # Thread friendly exit(status); - use threads 'exit' => 'threads_only'
-
Это глобально переопределяет поведение по умолчанию вызова
exit()внутри потока и эффективно заставляет такие вызовы вести себя так же, какthreads->exit(). Другими словами, с этим параметром вызовexit()завершает только данный поток.Из-за своего глобального эффекта этот параметр не следует использовать внутри модулей и т.п.
Главный поток не затрагивается этим параметром.
- threads->create({'exit' => 'thread_only'}, ...)
-
Это переопределяет поведение по умолчанию
exit()только внутри вновь созданного потока. - $thr->set_thread_exit_only(boolean)
-
Это можно использовать для изменения поведения «завершение только потока» для потока после его создания. С аргументом true,
exit()приведет к завершению только данного потока. С аргументом false,exit()завершит приложение.Главный поток не затрагивается этим вызовом.
- threads->set_thread_exit_only(boolean)
-
Метод класса для использования внутри потока для изменения собственного поведения при
exit().Главный поток не затрагивается этим вызовом.
СОСТОЯНИЕ ПОТОКА
Следующие булевы методы полезны для определения состояния потока.
- $thr->is_running()
-
Возвращает true, если поток всё ещё выполняется (т.е., если его функция входа ещё не завершена или не вышла).
- $thr->is_joinable()
-
Возвращает true, если поток завершил выполнение, не отсоединён и ещё не соединён. Другими словами, поток готов к соединению, и вызов
$thr->join()не будет блокировать. - $thr->is_detached()
-
Возвращает true, если поток был отсоединён.
- threads->is_detached()
-
Метод класса, который позволяет потоку определить, отсоединён ли он.
КОНТЕКСТ ПОТОКА
Как и с подпрограммами, тип значения, возвращаемого функцией входа потока, может быть определён контекстом потока: списком, скаляром или пустым значением. Контекст потока определяется при создании потока. Это необходимо, чтобы контекст был доступен функции входа через wantarray(). Поток затем может указать значение соответствующего типа, которое должно быть возвращено из ->join().
Явный контекст
Поскольку создание и присоединение потоков могут происходить в разных контекстах, может быть желательно указать контекст явно для функции входа потока. Это можно сделать, вызвав ->create() с ссылкой на хеш в качестве первого аргумента:
my $thr = threads->create({'context' => 'list'}, \&foo);
...
my @results = $thr->join(); В приведённом примере объект потока возвращается родительскому потоку в скалярном контексте, а функция входа потока foo вызывается в списочном (массиве) контексте, чтобы родительский поток мог получить список (массив) от вызова ->join(). ('array' синонимично 'list'.)
Аналогично, если вам нужен объект потока, но ваш поток не будет возвращать значение (т.е., контекст void), вы выполните следующее:
my $thr = threads->create({'context' => 'void'}, \&foo);
...
$thr->join(); Тип контекста также может использоваться в качестве ключа в ссылке на хеш, за которым следует значение true:
threads->create({'scalar' => 1}, \&foo);
...
my ($thr) = threads->list();
my $result = $thr->join(); Неявный контекст
Если явно не указан, контекст потока подразумевается из контекста вызова ->create():
# Create thread in list context
my ($thr) = threads->create(...);
# Create thread in scalar context
my $thr = threads->create(...);
# Create thread in void context
threads->create(...); $thr->wantarray()
Возвращает контекст потока таким же образом, как wantarray().
threads->wantarray()
Метод класса для возвращения контекста текущего потока. Он возвращает такое же значение, как и выполнение wantarray() внутри функции входа текущего потока.
РАЗМЕР СТЕКА ПОТОКА
Размер стека на поток по умолчанию для разных платформ значительно различается и почти всегда намного больше, чем необходимо для большинства приложений. В Win32 Makefile Perl явно устанавливает размер стека по умолчанию в 16 МБ; на большинстве других платформ используется системный размер по умолчанию, который снова может быть значительно больше, чем необходимо.
Настраивая размер стека более точно в соответствии с потребностями вашего приложения, вы можете значительно сократить использование памяти вашим приложением и увеличить количество одновременно запущенных потоков.
Обратите внимание, что в Windows размер кванта выделения адресного пространства составляет 64 КБ, поэтому установка стека меньше этого размера в Win32 Perl не сэкономит дополнительной памяти.
- threads->get_stack_size();
-
Возвращает текущий размер стека по умолчанию на поток. Значение по умолчанию равно нулю, что означает, что в настоящее время используется системный размер стека по умолчанию.
- $size = $thr->get_stack_size();
-
Возвращает размер стека для конкретного потока. Возвращаемое значение нуль указывает, что для потока использовался системный размер стека по умолчанию.
- $old_size = threads->set_stack_size($new_size);
-
Устанавливает новый размер стека по умолчанию на поток и возвращает предыдущее значение.
Некоторые платформы имеют минимальный размер стека потока. Попытка установить размер стека ниже этого значения приведёт к предупреждению, и будет использован минимальный размер стека.
Некоторые платформы Linux имеют максимальный размер стека. Установка слишком большого размера стека приведёт к ошибке создания потока.
При необходимости
$new_sizeбудет округлено до ближайшего кратного размеру страницы памяти (обычно 4096 или 8192).Потоки, созданные после установки размера стека, будут либо вызывать
pthread_attr_setstacksize()(для платформ pthreads), либо передавать размер стека вCreateThread()(для Win32 Perl).(Очевидно, этот вызов не влияет на уже существующие потоки.)
- use threads ('stack_size' => VALUE);
-
Это устанавливает размер стека по умолчанию на поток в начале приложения.
- $ENV{'PERL5_ITHREADS_STACK_SIZE'}
-
Размер стека по умолчанию на поток можно установить в начале приложения с помощью переменной окружения
PERL5_ITHREADS_STACK_SIZE:PERL5_ITHREADS_STACK_SIZE=1048576 export PERL5_ITHREADS_STACK_SIZE perl -e'use threads; print(threads->get_stack_size(), "\n")'Это значение переопределяет любой параметр
stack_sizeзаданный дляuse threads. Его основная цель — позволить задавать размер стека на поток для устаревших многопоточных приложений. - threads->create({'stack_size' => VALUE}, FUNCTION, ARGS)
-
Чтобы указать определённый размер стека для любого отдельного потока, вызовите
->create()со ссылкой на хеш в качестве первого аргумента:my $thr = threads->create({'stack_size' => 32*4096}, \&foo, @args); - $thr2 = $thr1->create(FUNCTION, ARGS)
-
Это создаёт новый поток (
$thr2), который наследует размер стека от существующего потока ($thr1). Это сокращение для следующего:my $stack_size = $thr1->get_stack_size(); my $thr2 = threads->create({'stack_size' => $stack_size}, FUNCTION, ARGS);
СИГНАЛИЗАЦИЯ ПОТОКОВ
Когда активна безопасная обработка сигналов (поведение по умолчанию — см. "Небезопасные сигналы" для получения дополнительных сведений), то сигналы могут быть отправлены и обработаны отдельными потоками.
- $thr->kill('SIG...');
-
Отправляет указанный сигнал в поток. Названия сигналов и (положительные) номера сигналов такие же, как и поддерживаемые kill(). Например, 'SIGTERM', 'TERM' и (в зависимости от ОС) 15 — все являются допустимыми аргументами для
->kill().Возвращает объект потока для возможности цепочек методов:
$thr->kill('SIG...')->join();
Обработчики сигналов должны быть настроены в потоках для сигналов, на которые они должны реагировать. Вот пример для отмены потока:
use threads;
sub thr_func
{
# Thread 'cancellation' signal handler
$SIG{'KILL'} = sub { threads->exit(); };
...
}
# Create a thread
my $thr = threads->create('thr_func');
...
# Signal the thread to terminate, and then detach
# it so that it will get cleaned up automatically
$thr->kill('KILL')->detach(); Вот ещё один упрощённый пример, который иллюстрирует использование сигнализации потоков в сочетании со светофором, чтобы обеспечить базовые возможности приостановки и возобновления:
use threads;
use Thread::Semaphore;
sub thr_func
{
my $sema = shift;
# Thread 'suspend/resume' signal handler
$SIG{'STOP'} = sub {
$sema->down(); # Thread suspended
$sema->up(); # Thread resumes
};
...
}
# Create a semaphore and pass it to a thread
my $sema = Thread::Semaphore->new();
my $thr = threads->create('thr_func', $sema);
# Suspend the thread
$sema->down();
$thr->kill('STOP');
...
# Allow the thread to continue
$sema->up(); ПРЕДУПРЕЖДЕНИЕ: Возможность сигнализации потоков, предоставляемая этим модулем, на самом деле не отправляет сигналы через ОС. Она эмулирует сигналы на уровне Perl, так что обработчики сигналов вызываются в соответствующем потоке. Например, отправка $thr->kill('STOP') на самом деле не приостанавливает поток (или весь процесс), но вызывает обработчик $SIG{'STOP'} в данном потоке (как показано выше).
Таким образом, сигналы, которые обычно не подходят для использования в команде kill() (например, kill('KILL', $$)) допустимы для использования с методом ->kill() (опять же, как показано выше).
Соответственно, отправка сигнала в поток не прерывает выполняемую в данный момент операцию потока: сигнал будет обработан после завершения текущей операции. Например, если поток застрял на вызове ввода-вывода, отправка ему сигнала не приведёт к прерыванию вызова ввода-вывода, чтобы сигнал был обработан немедленно.
Отправка сигнала в завершённому/закончившемуся потоку игнорируется.
ПРЕДУПРЕЖДЕНИЯ
- Perl завершил работу с активными потоками:
-
Если программа завершается, не дождавшись присоединения или отсоединения всех потоков, то будет выведено это предупреждение.
ПРИМЕЧАНИЕ: Если основной поток завершается, то это предупреждение нельзя подавить, используя
no warnings 'threads';, как показано ниже. - Ошибка создания потока: pthread_create вернул #
-
Обратитесь к соответствующей странице man для
pthread_create, чтобы определить истинную причину ошибки. - Поток # завершился аномально: ...
-
Поток завершился иным способом, чем просто возвращением из своей функции входа или с помощью
threads->exit(). Например, поток мог завершиться из-за ошибки или с помощьюdie. - Использование минимального размера стека потока #
-
Некоторые платформы имеют минимальный размер стека потока. Попытка установить размер стека ниже этого значения приведет к вышеуказанному предупреждению, и размер стека будет установлен на минимальное значение.
- Ошибка создания потока: pthread_attr_setstacksize(SIZE) вернул 22
-
Указанный SIZE превышает максимальный размер стека системы. Используйте меньшее значение для размера стека.
При необходимости предупреждения о потоках можно подавить, используя:
no warnings 'threads'; в соответствующем контексте.
ОШИБКИ
- Этот Perl не скомпилирован для поддержки потоков
-
Конкретная копия Perl, которую вы пытаетесь использовать, не была скомпилирована с опцией конфигурации
useithreads.Для поддержки потоков необходимо перекомпилировать весь Perl и все XS-модули в установке Perl; это не просто вопрос добавления модуля threads (т.е., Perl с поддержкой потоков и без неё имеют бинарную несовместимость).
- Невозможно изменить размер стека существующего потока
-
Размер стека существующих потоков изменить нельзя, поэтому следующее действие приводит к вышеописанной ошибке:
$thr->set_stack_size($size); - Невозможно послать сигналы потокам без безопасных сигналов
-
Для использования метода сигнализации
->kill()должны быть активны безопасные сигналы. Подробнее см. "Небезопасные сигналы". - Неизвестное имя сигнала: ...
-
Конкретная копия Perl, которую вы пытаетесь использовать, не поддерживает указанный сигнал, используемый в вызове
->kill().
ОШИБКИ И ОГРАНИЧЕНИЯ
Перед тем, как отправить сообщение об ошибке, пожалуйста, ознакомьтесь с существующими проблемами, возможно, разместив сообщение на форуме обсуждений.
- Модули, безопасные для потоков
-
См. "Создание потокобезопасного модуля" в perlmod, когда создаете модули, которые могут использоваться в многопоточных приложениях, особенно если эти модули используют не-Perl данные или код XS.
- Использование модулей, небезопасных для потоков
-
К сожалению, вы можете столкнуться с Perl-модулями, которые не являются потокобезопасными. Например, они могут привести к аварийному завершению интерпретатора Perl во время выполнения или к сбою с дампом ядра при завершении. В зависимости от модуля и требований вашего приложения, возможно обойти такие трудности.
Если модуль будет использоваться только внутри потока, вы можете попробовать загрузить модуль изнутри функции точки входа потока, используя
require(иimport, если необходимо):sub thr_func { require Unsafe::Module # Unsafe::Module->import(...); .... }Если модуль необходим внутри главного потока, попробуйте изменить ваше приложение таким образом, чтобы модуль загружался (снова используя
requireи->import()) после запуска всех потоков и таким образом, чтобы после этого не запускались другие потоки.Если вышеперечисленное не работает или не подходит для вашего приложения, отправьте отчет об ошибке на https://rt.cpan.org/Public/ для проблемного модуля.
- Потребление памяти
-
На большинстве систем частая и непрерывная создание и уничтожение потоков может привести к постоянному росту потребления памяти интерпретатором Perl. Хотя просто запустить потоки и затем
->join()или->detach()их просто, для долгоживущих приложений лучше поддерживать пул потоков и повторно использовать их для необходимой работы, используя очереди для уведомления потоков о запланированной работе. Распространение этого модуля на CPAN содержит простой пример (examples/pool_reuse.pl), иллюстрирующий создание, использование и мониторинг пула повторно используемых потоков. - Текущая рабочая директория
-
На всех платформах, кроме MSWin32, установка текущей рабочей директории делится между всеми потоками, так что изменение ее в одном потоке (например, используя
chdir()) повлияет на все потоки в приложении.На MSWin32 каждый поток сохраняет собственное значение текущей рабочей директории.
- Локали
-
До Perl 5.28 локали нельзя было использовать с потоками из-за различных гонок. Начиная с этой версии, на системах, которые реализуют потокобезопасные функции локали, можно использовать потоки с некоторыми оговорками. Это включает Windows, начиная с Visual Studio 2005, и системы, совместимые с POSIX 2008. См. "Многопоточная работа" в perllocale.
Каждый поток (кроме главного потока) запускается с локалью C. Главный поток запускается как и все другие Perl-программы; см. "ENVIRONMENT" в perllocale. Вы можете переключать локали в любом потоке так часто, как вам нужно.
Если вы хотите унаследовать локаль родительского потока, вы можете в родительском потоке установить переменную следующим образом:
$foo = POSIX::setlocale(LC_ALL, NULL);а затем передать в threads->create() подпрограмму, которая использует
$foo. Затем в дочернем потоке вы говоритеPOSIX::setlocale(LC_ALL, $foo);Или вы можете использовать средства в threads::shared для передачи
$foo; или если среда не изменилась, в дочернем потоке сделайтеPOSIX::setlocale(LC_ALL, ""); - Переменные окружения
-
В настоящее время на всех платформах, кроме MSWin32, все системные вызовы (например, используя
system()или обратные кавычки), сделанные из потоков, используют настройки переменных окружения из главного потока. Другими словами, изменения, внесенные в%ENVв потоке, не будут видны в системных вызовах, сделанных этим потоком.Чтобы обойти это, установите переменные окружения как часть системного вызова. Например:
my $msg = 'hello'; system("FOO=$msg; echo \$FOO"); # Outputs 'hello' to STDOUTНа MSWin32 каждый поток поддерживает свой собственный набор переменных окружения.
- Перехват сигналов
-
Сигналы перехватываются главным потоком (ID потока = 0) сценария. Поэтому установка обработчиков сигналов в потоках для целей, отличных от "ОБРАБОТКА СИГНАЛОВ В ПОТОКАХ", как описано выше, не приведет к желаемому результату.
Это особенно верно, если вы пытаетесь перехватить
SIGALRMв потоке. Для обработки сигналов таймера в потоках настройте обработчик сигналов в главном потоке, а затем используйте "ОБРАБОТКА СИГНАЛОВ В ПОТОКАХ" для перенаправления сигнала в поток:# Create thread with a task that may time out my $thr = threads->create(sub { threads->yield(); eval { $SIG{ALRM} = sub { die("Timeout\n"); }; alarm(10); ... # Do work here alarm(0); }; if ($@ =~ /Timeout/) { warn("Task in thread timed out\n"); } }; # Set signal handler to relay SIGALRM to thread $SIG{ALRM} = sub { $thr->kill('ALRM') }; ... # Main thread continues working - Родительские и дочерние потоки
-
На некоторых платформах может быть невозможно уничтожить родительские потоки, пока существуют дочерние потоки.
- Небезопасные сигналы
-
Начиная с Perl 5.8.0, сигналы были сделаны безопаснее в Perl, отложив их обработку до тех пор, пока интерпретатор не окажется в безопасном состоянии. См. "Безопасные сигналы" в perl58delta и "Отложенные сигналы (безопасные сигналы)" в perlipc для получения дополнительной информации.
Безопасные сигналы являются по умолчанию, и старое, немедленное, небезопасное поведение сигнализации действует только в следующих ситуациях:
-
Perl был скомпилирован с
PERL_OLD_SIGNALS(см.perl -V). -
Переменная среды
PERL_SIGNALSустановлена в значениеunsafe(см. "PERL_SIGNALS" в perlrun). -
Используется модуль Perl::Unsafe::Signals.
Если небезопасные сигналы включены, то обработка сигналов не является потокобезопасной, и метод сигнализации
->kill()использовать нельзя. -
- Идентичность объектов, возвращаемых из потоков
-
Когда значение возвращается из потока с помощью операции
join, значение и все, на что оно ссылается, копируются в присоединяющийся поток, почти так же, как копируются значения при создании потока. Это работает нормально для большинства типов значений, включая массивы, хэши и подпрограммы. Копирование рекурсивно проходит через элементы массива, скаляры ссылок, переменные, закрытые подпрограммами, и другие типы ссылок.Однако всё, на что ссылается возвращаемое значение, представляет собой новую копию в присоединяющемся потоке, даже если возвращаемый объект в дочернем потоке был копией чего-то, что ранее существовало в родительском потоке. После присоединения родительский поток, следовательно, будет иметь дубликат каждого такого объекта. Это иногда имеет значение, особенно если объект изменяется; это особенно важно для частных данных, к которым возвращаемая подпрограмма предоставляет доступ.
- Возврат благословлённых объектов из потоков
-
Возврат благословлённых объектов из потоков не работает. В зависимости от участвующих классов, вы можете обойти это, вернув сериализованную версию объекта (например, используя Data::Dumper или Storable), а затем восстановить его в присоединяющемся потоке. Если вы используете Perl 5.10.0 или более позднюю версию, и если класс поддерживает объекты совместного использования, вы можете передать их через общие очереди.
- Блоки END в потоках
-
Можно добавить блоки END в потоки, используя require или eval с соответствующим кодом. Эти
ENDблоки будут затем выполнены, когда интерпретатор потока будет уничтожен (т.е., либо во время вызова->join(), или при завершении программы).Однако вызов любых методов threads в таком
ENDблоке, скорее всего, не удастся (например, приложение может зависнуть или сгенерировать ошибку) из-за мьютексов, которые необходимы для управления функциональностью в модуле threads.По этой причине использование
ENDблоков в потоках настоятельно не рекомендуется. - Открытые дескрипторы каталогов
-
В perl 5.14 и выше, на системах, отличных от Windows, которые не поддерживают функцию C
fchdir, дескрипторы каталогов (см. opendir) не будут копироваться в новые потоки. Вы можете использовать переменнуюd_fchdirв Config.pm, чтобы определить, поддерживает ли ваша система эту функцию.В предыдущих версиях perl создание потоков с открытыми дескрипторами каталогов приводило к аварийному завершению интерпретатора. [perl #75154]
- Отсоединенные потоки и глобальное уничтожение
-
Если главный поток завершается, в то время как существуют отсоединенные потоки, которые все еще работают, то фаза глобального уничтожения Perl не выполняется, поскольку в противном случае некоторые глобальные структуры, которые управляют работой потоков и которые выделены в памяти главного потока, могут быть уничтожены до уничтожения отсоединенного потока.
Если вы используете какой-либо код, который требует выполнения фазы глобального уничтожения для очистки (например, удаления временных файлов), не используйте отсоединенные потоки, а присоедините все потоки перед завершением программы.
- Ошибки Perl и версия threads из CPAN
-
Поддержка потоков выходит за рамки кода в этом модуле (т.е., threads.pm и threads.xs) и входит в сам интерпретатор Perl. Более старые версии Perl содержат ошибки, которые могут проявляться, несмотря на использование последней версии threads из CPAN. Для этого нет решения, кроме обновления до последней версии Perl.
Даже с последней версией Perl известно, что определенные конструкции с потоками могут привести к появлению предупреждений о потерянных скалярах или неиспользуемых скалярах. Однако такие предупреждения не представляют опасности и могут быть безопасно проигнорированы.
Вы можете искать отчеты об ошибках, связанные с threads, на https://rt.cpan.org/Public/. При необходимости отправляйте любые новые ошибки, проблемы, исправления и т.д. на https://rt.cpan.org/Public/Dist/Display.html?Name=threads
ТРЕБОВАНИЯ
Perl 5.8.0 или более поздняя версия
См. также
threads на MetaCPAN: https://metacpan.org/release/threads
Репозиторий кода для распространения CPAN: https://github.com/Dual-Life/threads
https://www.perl.com/pub/a/2002/06/11/threads.html и https://www.perl.com/pub/a/2002/09/04/threads.html
Список рассылки Perl threads: https://lists.perl.org/list/ithreads.html
Обсуждение размера стека: https://www.perlmonks.org/?node_id=532956
Образец кода в каталоге examples этого дистрибутива на CPAN.
АВТОР
Артур Бергман <sky AT crucially DOT net>
Версия CPAN, подготовленная Джерри Д. Хедденом <jdhedden AT cpan DOT org>
ЛИЦЕНЗИЯ
threads распространяется по той же лицензии, что и Perl.
БЛАГОДАРНОСТИ
Ричард Содерберг <perl AT crystalflame DOT net> - Оказал большую помощь, пытаясь найти причины гонок и других странных ошибок!
Саймон Коузенс <simon AT brecon DOT co DOT uk> - Отвечал на миллионы надоедливых вопросов
Рокко Капуто <troc AT netrus DOT net>
Випул Вед Пракаш <mail AT vipul DOT net> - Помогал с отладкой
Дийн Арнольд <darnold AT presicient DOT com> - API размера стека
© 1993–2023 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.38.0/threads