threads
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- ПРЕДУПРЕЖДЕНИЕ
- СИНТЕКС
- ОПИСАНИЕ
- ВЫХОД ИЗ ПОТОКА
- СОСТОЯНИЕ ПОТОКА
- КОНТЕКСТ ПОТОКА
- РАЗМЕР СТЕКА ПОТОКА
- СИГНАЛИЗАЦИЯ ПОТОКОВ
- ПРЕДУПРЕЖДЕНИЯ
- ОШИБКИ
- ОШИБКИ И ОГРАНИЧЕНИЯ
- ТРЕБОВАНИЯ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- ЛИЦЕНЗИЯ
- БЛАГОДАРНОСТИ
НАЗВАНИЕ
threads - потоки Perl, основанные на интерпретаторе
ВЕРСИЯ
В этом документе описаны потоки версии 2.26
ПРЕДУПРЕЖДЕНИЕ
«Потоки, основанные на интерпретаторе», предоставляемые 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()
-
Возвращает идентификатор потока. Идентификаторы потоков — это уникальные целые числа, основной поток в программе имеет ID 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 — undef. - threads->yield()
-
Это предложение операционной системе позволить этому потоку уступить процессорное время другим потокам. То, что фактически происходит, в значительной степени зависит от реализации потоков на уровне операционной системы.
Вы можете
use threads qw(yield), а затем просто использоватьyield()в своём коде. - threads->list()
- threads->list(threads::all)
- threads->list(threads::running)
- threads->list(threads::joinable)
-
Без аргументов (или с использованием
threads::all) и в контексте списка возвращает список всех не соединённых, не отсоединённых объектов threads. В скалярном контексте возвращает количество таких объектов.С истинным аргументом (используя
threads::running) возвращает список всех не соединённых, не отсоединённых объектов threads, которые всё ещё выполняются.С ложным аргументом (используя
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в скалярном контексте или пустой список в контексте списка.При вызове из основного потока (main) это работает так же, как
exit(0). - threads->exit(status)
-
При вызове из потока это работает как
threads->exit()(т.е. код выхода игнорируется).При вызове из основного потока (main) это работает так же, как
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()приводит только к завершению потока.Из-за своего глобального влияния этот параметр не следует использовать внутри модулей и тому подобного.
Основной поток (main) не затрагивается этим параметром.
- threads->create({'exit' => 'thread_only'}, ...)
-
Это переопределяет поведение по умолчанию
exit()только внутри вновь созданного потока. - $thr->set_thread_exit_only(boolean)
-
Это можно использовать для изменения поведения "завершить только поток" для потока после его создания. С аргументом true,
exit()приведет только к завершению потока. С аргументом false,exit()приведет к завершению приложения.Основной поток (main) не затрагивается этим вызовом.
- threads->set_thread_exit_only(boolean)
-
Метод класса для использования внутри потока для изменения его поведения при
exit().Основной поток (main) не затрагивается этим вызовом.
СОСТОЯНИЕ ПОТОКА
Следующие булевы методы полезны для определения состояния потока.
- $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'.)
Аналогично, если вам нужен объект потока, но ваш поток не будет возвращать значение (т.е. контекст пустоты), вы бы сделали следующее:
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 во время выполнения или выдать ошибку core dump при завершении. В зависимости от модуля и требований вашего приложения, возможно, удастся обойти такие трудности.
Если модуль будет использоваться только внутри потока, вы можете попробовать загрузить модуль изнутри функции точки входа в поток, используя
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; см. "СРЕДА" в 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–2021 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.34.0/threads