threads
СОДЕРЖАНИЕ
- ИМЯ
- ВЕРСИЯ
- ПРЕДУПРЕЖДЕНИЕ
- СИНОПСИС
- ОПИСАНИЕ
- ВЫХОД ИЗ ПОТОКА
- СОСТОЯНИЕ ПОТОКА
- КОНТЕКСТ ПОТОКА
- РАЗМЕР СТЕКА ПОТОКА
- СИГНАЛИЗАЦИЯ ПОТОКА
- ПРЕДУПРЕЖДЕНИЯ
- ОШИБКИ
- ОШИБКИ И ОГРАНИЧЕНИЯ
- ТРЕБОВАНИЯ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- ЛИЦЕНЗИЯ
- БЛАГОДАРНОСТИ
ИМЯ
threads - потоки Perl, основанные на интерпретаторе
ВЕРСИЯ
В этом документе описывается версия потоков 2.21
ПРЕДУПРЕЖДЕНИЕ
«Потоки, основанные на интерпретаторе», предоставляемые Perl, не являются быстрой и лёгкой системой многозадачности, которую можно ожидать или надеяться получить. Потоки реализованы таким образом, что их легко неправильно использовать. Мало кто знает, как их правильно использовать или сможет помочь.
Использование потоков, основанных на интерпретаторе Perl, официально не рекомендуется.
СИНОПСИС
use threads ('yield',
'stack_size' => 64*4096,
'exit' => 'threads_only',
'stringify');
sub start_thread {
my @args = @_;
print('Thread started: ', join(' ', @args), "\n");
}
my $thr = threads->create('start_thread', 'argument');
$thr->join();
threads->create(sub { print("I am a thread\n"); })->join();
my $thr2 = async { foreach (@files) { ... } };
$thr2->join();
if (my $err = $thr2->error()) {
warn("Thread error: $err\n");
}
# Invoke thread in list context (implicit) so it can return a list
my ($thr) = threads->create(sub { return (qw/a b c/); });
# or specify list context explicitly
my $thr = threads->create({'context' => 'list'},
sub { return (qw/a b c/); });
my @results = $thr->join();
$thr->detach();
# Get a thread's object
$thr = threads->self();
$thr = threads->object($tid);
# Get a thread's ID
$tid = threads->tid();
$tid = $thr->tid();
$tid = "$thr";
# Give other threads a chance to run
threads->yield();
yield();
# Lists of non-detached threads
my @threads = threads->list();
my $thread_count = threads->list();
my @running = threads->list(threads::running);
my @joinable = threads->list(threads::joinable);
# Test thread objects
if ($thr1 == $thr2) {
...
}
# Manage thread stack size
$stack_size = threads->get_stack_size();
$old_size = threads->set_stack_size(32*4096);
# Create a thread with a specific context and stack size
my $thr = threads->create({ 'context' => 'list',
'stack_size' => 32*4096,
'exit' => 'thread_only' },
\&foo);
# Get thread's context
my $wantarray = $thr->wantarray();
# Check thread's state
if ($thr->is_running()) {
sleep(1);
}
if ($thr->is_joinable()) {
$thr->join();
}
# Send a signal to a thread
$thr->kill('SIGUSR1');
# Exit a thread
threads->exit(); ОПИСАНИЕ
С Perl 5.8 появилась возможность программирования с помощью потоков, используя модель интерпретаторных потоков, которая создаёт новый интерпретатор Perl для каждого потока и, по умолчанию, не приводит к совместному использованию данных или состояния между потоками.
(До Perl 5.8 существовали 5005threads через Thread.pm API. Эта модель потоков устарела и была удалена в Perl 5.10.0.)
Как только что упоминалось, все переменные по умолчанию локальны для потока. Для использования общих переменных, вам необходимо также загрузить threads::shared:
use threads;
use threads::shared; При загрузке threads::shared, необходимо use threads перед use threads::shared. (threads выдаст предупреждение, если вы сделаете это наоборот.)
Настоятельно рекомендуется включить потоки через use threads как можно раньше в вашем скрипте.
При необходимости скрипты могут быть написаны так, чтобы работать как с потоками, так и без них в Perl:
my $can_use_threads = eval 'use threads; 1';
if ($can_use_threads) {
# Do processing using threads
...
} else {
# Do it without using threads
...
} - $thr = threads->create(FUNCTION, ARGS)
-
Это создаст новый поток, который начнёт выполнение со указанной точкой входа, функции, и передаст ей список ARGS в качестве параметров. Он вернёт соответствующий объект потока, или
undefесли создание потока не удалось.FUNCTION может быть именем функции, анонимной подпрограммой или ссылкой на код.
my $thr = threads->create('func_name', ...); # or my $thr = threads->create(sub { ... }, ...); # or my $thr = threads->create(\&func, ...);Метод
->new()является псевдонимом для->create(). - $thr->join()
-
Это ожидает завершения соответствующего потока. Когда поток завершится,
->join()вернёт значение(я) возвращаемой функции точки входа.Контекст (пустой, скалярный или список) для значения(й) возврата для
->join()определяется при создании потока.# Create thread in list context (implicit) my ($thr1) = threads->create(sub { my @results = qw(a b c); return (@results); }); # or (explicit) my $thr1 = threads->create({'context' => 'list'}, sub { my @results = qw(a b c); return (@results); }); # Retrieve list results from thread my @res1 = $thr1->join(); # Create thread in scalar context (implicit) my $thr2 = threads->create(sub { my $result = 42; return ($result); }); # Retrieve scalar result from thread my $res2 = $thr2->join(); # Create a thread in void context (explicit) my $thr3 = threads->create({'void' => 1}, sub { print("Hello, world\n"); }); # Join the thread in void context (i.e., no return value) $thr3->join();См. «КОНТЕКСТ ПОТОКА» для получения дополнительной информации.
Если программа завершается, не дожидаясь, пока все потоки будут объединены или отделены, то будет выведено предупреждение.
Вызов
->join()или->detach()для уже объединённого потока приведёт к ошибке. - $thr->detach()
-
Делает поток несоединяемым и приводит к тому, что любое возвращаемое значение будет отброшено. Когда программа завершается, любые отделённые потоки, которые ещё работают, безмолвно завершаются.
Если программа завершается, не дожидаясь, пока все потоки будут объединены или отделены, то будет выведено предупреждение.
Вызов
->join()или->detach()для уже отделённого потока приведёт к ошибке. - threads->detach()
-
Метод класса, который позволяет потоку отсоединиться.
- threads->self()
-
Метод класса, который позволяет потоку получить свой собственный объект threads.
- $thr->tid()
-
Возвращает идентификатор потока. Идентификаторы потоков являются уникальными целыми числами, где главный поток в программе имеет значение 0, а каждое созданное дополнительно — увеличивается на 1.
- threads->tid()
-
Метод класса, который позволяет потоку получить свой собственный идентификатор.
- "$thr"
-
Если вы добавите опцию
stringifyк вашему объявлениюuse threads, то использование объекта потока в строке или контексте строки (например, в качестве ключа хэша) приведёт к использованию его идентификатора в качестве значения:use threads qw(stringify); my $thr = threads->create(...); print("Thread $thr started\n"); # Prints: Thread 1 started - threads->object($tid)
-
Это вернёт объект threads для активного потока, связанного с указанным идентификатором потока. Если
$tidявляется значением для текущего потока, то этот вызов работает так же, как->self(). В противном случае возвращаетundefесли нет потока, связанного с TID, если поток объединён или отделён, если не указан TID или указанный TID имеет значение 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 во время выполнения или к сбросу ядра при завершении. В зависимости от модуля и требований вашего приложения, возможно обойти такие трудности.
Если модуль будет использоваться только внутри потока, вы можете попробовать загрузить модуль изнутри функции точки входа в поток, используя
require(иimportпри необходимости):sub thr_func { require Unsafe::Module # Unsafe::Module->import(...); .... }Если модуль необходим внутри главного потока, попробуйте изменить ваше приложение так, чтобы модуль загружался (опять же, используя
requireи->import()) после запуска любых потоков и таким образом, чтобы после этого не запускались другие потоки.Если вышеупомянутое не работает или не подходит для вашего приложения, отправьте отчет об ошибке на http://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 locale. Главный поток запускается как все другие программы 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, которые не поддерживают функцию
fchdirC, дескрипторы каталогов (см. opendir) не будут копироваться в новые потоки. Вы можете использовать переменнуюd_fchdirв Config.pm, чтобы определить, поддерживает ли ваша система эту функцию.В предыдущих версиях Perl запуск потоков с открытыми дескрипторами каталогов приводил к сбою интерпретатора. [perl #75154]
- Откреплённые потоки и глобальное уничтожение
-
Если главный поток завершается, в то время как есть откреплённые потоки, которые всё ещё выполняются, то глобальная фаза уничтожения Perl не выполняется, так как в противном случае некоторые глобальные структуры, которые контролируют работу потоков и которые выделены в памяти главного потока, могут быть уничтожены до уничтожения откреплённого потока.
Если вы используете любой код, который требует выполнения глобальной фазы уничтожения для очистки (например, удаление временных файлов), не используйте откреплённые потоки, а вместо этого присоедините все потоки перед выходом из программы.
- Ошибки Perl и версия threads с CPAN
-
Поддержка потоков выходит за рамки кода в этом модуле (т.е., threads.pm и threads.xs) и в сам интерпретатор Perl. Более старые версии Perl содержат ошибки, которые могут проявляться, несмотря на использование последней версии threads с CPAN. Для этого нет решения, кроме обновления до последней версии Perl.
Даже с последней версией Perl известно, что некоторые конструкции с потоками могут приводить к сообщениям об ошибках о утечках скаляров или неиспользуемых скалярах. Однако такие предупреждения безопасны и могут быть проигнорированы.
Вы можете искать связанные с threads сообщения об ошибках на http://rt.cpan.org/Public/. Если необходимо, отправляйте новые ошибки, проблемы, исправления и т.д. по адресу: http://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
http://www.perl.com/pub/a/2002/06/11/threads.html и http://www.perl.com/pub/a/2002/09/04/threads.html
Список рассылки Perl threads: http://lists.perl.org/list/ithreads.html
Обсуждение размера стека: http://www.perlmonks.org/?node_id=532956
Пример кода в каталоге examples этого дистрибутива на CPAN.
АВТОР
Artur Bergman <sky AT crucially DOT net>
Версия CPAN, подготовленная Jerry D. Hedden <jdhedden AT cpan DOT org>
ЛИЦЕНЗИЯ
threads распространяется под той же лицензией, что и Perl.
БЛАГОДАРНОСТИ
Richard Soderberg <perl AT crystalflame DOT net> - Огромная помощь в поиске причин гонок и других странных багов!
Simon Cozens <simon AT brecon DOT co DOT uk> - Отвечал на множество надоедливых вопросов
Rocco Caputo <troc AT netrus DOT net>
Vipul Ved Prakash <mail AT vipul DOT net> - Помощь в отладке
Dean Arnold <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.30.3/threads