threads
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- ПРЕДУПРЕЖДЕНИЕ
- СИНОПСИС
- ОПИСАНИЕ
- ВЫХОД ИЗ ПОТОКА
- СОСТОЯНИЕ ПОТОКА
- КОНТЕКСТ ПОТОКА
- РАЗМЕР СТЕКА ПОТОКА
- СИГНАЛИЗАЦИЯ ПОТОКОВ
- ПРЕДУПРЕЖДЕНИЯ
- ОШИБКИ
- ОШИБКИ И ОГРАНИЧЕНИЯ
- ТРЕБОВАНИЯ
- СМОТРИ ТАКЖЕ
- АВТОР
- ЛИЦЕНЗИЯ
- БЛАГОДАРНОСТИ
НАЗВАНИЕ
threads - потоки Perl, основанные на интерпретаторе
ВЕРСИЯ
В данном документе описывается версия потоков 2.25
ПРЕДУПРЕЖДЕНИЕ
«Потоки, основанные на интерпретаторе», предоставляемые 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 был доступен 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 равен 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. В скалярном контексте возвращает количество таких же.С аргументом 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в скалярном контексте или пустой список в контексте списка.При вызове из основного потока (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 при завершении. В зависимости от модуля и требований вашего приложения, возможно найти обходные пути таких трудностей.
Если модуль будет использоваться только внутри потока, вы можете попробовать загрузить модуль изнутри функции точки входа в поток, используя
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 каждый поток поддерживает свой собственный набор переменных окружения.
- Перехват сигналов
-
Сигналы перехватываются основным потоком (идентификатор потока = 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
END_OF_DOCUMENT_MARKERСписок рассылки 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–2020 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.32.0/threads