threads
СОДЕРЖАНИЕ
- ИМЯ
- ВЕРСИЯ
- ПРЕДУПРЕЖДЕНИЕ
- СИНОПСИС
- ОПИСАНИЕ
- ВЫХОД ИЗ ПОТОКА
- СОСТОЯНИЕ ПОТОКА
- КОНТЕКСТ ПОТОКА
- РАЗМЕР СТЕКА ПОТОКА
- СИГНАЛИЗАЦИЯ ПОТОКОВ
- ПРЕДУПРЕЖДЕНИЯ
- ОШИБКИ
- ОШИБКИ И ОГРАНИЧЕНИЯ
- ТРЕБОВАНИЯ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- ЛИЦЕНЗИЯ
- БЛАГОДАРНОСТИ
ИМЯ
threads - Потоки Perl, основанные на интерпретаторе
ВЕРСИЯ
В данном документе описываются потоки версии 2.27
ПРЕДУПРЕЖДЕНИЕ
«Потоки», реализованные в 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 как можно раньше в вашем скрипте.
Если необходимо, скрипты могут быть написаны таким образом, чтобы работать как с потоками, так и без них:
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». В скалярном контексте возвращает счет таких.С истинным аргументом (используя
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()
-
Этот частный метод возвращает указатель (т.е., местоположение памяти, выраженное как целое без знака) на внутреннюю структуру потока, связанную с объектом «threads». Для 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'.)
Аналогично, если вам нужен объект потока, но ваш поток не будет возвращать значение (т.е. контекст пустое значение), вы бы сделали следующее:
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();
-
Возвращает размер стека для конкретного потока. Значение 0 указывает, что для потока был использован системный размер стека по умолчанию.
- $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, которые не поддерживают функцию
fchdirC, дескрипторы каталогов (см. 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 mailing list: https://lists.perl.org/list/ithreads.html
Обсуждение размера стека: https://www.perlmonks.org/?node_id=532956
END_OF_DOCUMENT_MARKERОбразец кода в каталоге примеры этого дистрибутива на 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.36.0/threads