Spec-Zone.ru › Perl 5.28

threads

СОДЕРЖАНИЕ

  • ИМЯ
  • ВЕРСИЯ
  • ПРЕДУПРЕЖДЕНИЕ
  • СИНОПСИС
  • ОПИСАНИЕ
  • ВЫХОД ИЗ ПОТОКА
  • СОСТОЯНИЕ ПОТОКА
  • КОНТЕКСТ ПОТОКА
    • Явный контекст
    • Неявный контекст
    • $thr->wantarray()
    • threads->wantarray()
  • РАЗМЕР СТЕКА ПОТОКА
  • СИГНАЛИЗАЦИЯ ПОТОКА
  • ПРЕДУПРЕЖДЕНИЯ
  • ОШИБКИ
  • ОШИБКИ И ОГРАНИЧЕНИЯ
  • ТРЕБОВАНИЯ
  • СМОТРИТЕ ТАКЖЕ
  • АВТОР
  • ЛИЦЕНЗИЯ
  • БЛАГОДАРНОСТИ

ИМЯ

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 в скалярном контексте или пустого списка в контексте списка.

Когда вызывается из основной нити, это ведет себя так же, как 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();

Возвращает размер стека для конкретного потока. Возвращаемое значение ноль указывает, что для потока использовался системный размер стека по умолчанию.

$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 (то есть, перлы с поддержкой потоков и без нее несовместимы на бинарном уровне).

Невозможно изменить размер стека существующего потока

Размер стека существующих потоков изменить нельзя, поэтому следующее приведет к вышеуказанной ошибке:

$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()) после запуска любых потоков и таким образом, чтобы после этого не запускались другие потоки.

Если вышеперечисленное не работает или не подходит для вашего приложения, подайте отчет об ошибке на 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-локали. Главный поток запускается как и все остальные программы Perl; см. "ENVIRONMENT" в perllocale. Вы можете переключать локали в любом потоке так часто, как захотите.

Если вы хотите унаследовать локаль родительского потока, вы можете в родительском потоке установить переменную следующим образом:

$foo = POSIX::setlocale(LC_ALL, NULL);

а затем передать threads->create() подпрограмму, которая закрывает $foo. Затем в дочернем потоке вы говорите

POSIX::setlocale(LC_ALL, $foo);

Или вы можете использовать средства в threads::shared для передачи $foo; или если среда не изменилась, в дочернем потоке сделайте

POSIX::setlocale(LC_ALL, "");
Переменные среды

В настоящее время на всех платформах, кроме MSWin32, все системные вызовы (например, с использованием system() или обратных кавычек), сделанные из потоков, используют настройки переменных среды из главного потока. Другими словами, изменения, внесенные в %ENV в потоке, не будут видны в системных вызовах, сделанных этим потоком.

Для обхода этой проблемы установите переменные среды как часть системного вызова. Например:

my $msg = 'hello';
system("FOO=$msg; echo \$FOO");   # Outputs 'hello' to STDOUT

На MSWin32 каждый поток сохраняет свой собственный набор переменных среды.

Перехват сигналов

Сигналы перехватываются главным потоком (ID потока = 0) сценария. Поэтому настройка обработчиков сигналов в потоках для целей, отличных от "ОБРАБОТКА СИГНАЛОВ В ПОТОКАХ", как описано выше, не приведет к желаемому результату.

Это особенно верно, если пытаетесь перехватить SIGALRM в потоке. Для обработки сигналов тревоги в потоках настройте обработчик сигналов в главном потоке, а затем используйте "ОБРАБОТКА СИГНАЛОВ В ПОТОКАХ" для передачи сигнала потоку:

# Create thread with a task that may time out
my $thr = threads->create(sub {
    threads->yield();
    eval {
        $SIG{ALRM} = sub { die("Timeout\n"); };
        alarm(10);
        ...  # Do work here
        alarm(0);
    };
    if ($@ =~ /Timeout/) {
        warn("Task in thread timed out\n");
    }
};

# Set signal handler to relay SIGALRM to thread
$SIG{ALRM} = sub { $thr->kill('ALRM') };

... # Main thread continues working
Родительские и дочерние потоки

На некоторых платформах может быть невозможно уничтожить родительские потоки, пока существуют дочерние потоки.

Небезопасные сигналы

Начиная с Perl 5.8.0, сигналы были сделаны безопаснее в Perl, откладывая их обработку до тех пор, пока интерпретатор не окажется в безопасном состоянии. См. "Безопасные сигналы" в perl58delta и "Отложенные сигналы (безопасные сигналы)" в perlipc для получения дополнительной информации.

Безопасные сигналы — это поведение по умолчанию, а старое, непосредственное, небезопасное поведение сигнализации действует только в следующих ситуациях:

  • Perl был скомпилирован с PERL_OLD_SIGNALS (см. perl -V).

  • Переменная среды PERL_SIGNALS установлена в unsafe (см. "PERL_SIGNALS" в perlrun).

  • Используется модуль Perl::Unsafe::Signals.

Если небезопасные сигналы действуют, то обработка сигналов не безопасна для многопоточного использования, и метод сигнализации ->kill() не может быть использован.

Идентификатор объектов, возвращаемых из потоков

Когда значение возвращается из потока посредством операции join, значение и все, на что оно ссылается, копируются в присоединяющийся поток аналогичным образом, как копируются значения при создании потока. Это работает хорошо для большинства типов значений, включая массивы, хэши и подпрограммы. Копирование рекурсивно проходит по элементам массива, скалярам ссылок, переменным, закрытым подпрограммами, и другим типам ссылок.

Однако всё, на что ссылается возвращаемое значение, является новой копией в присоединяющемся потоке, даже если возвращаемый объект в дочернем потоке был копией чего-то, что ранее существовало в родительском потоке. После присоединения родительский поток, следовательно, будет иметь дубликат каждого такого объекта. Это иногда имеет значение, особенно если объект претерпевает мутации; это особенно важно для частных данных, к которым возвращаемая подпрограмма предоставляет доступ.

Возврат освящённых объектов из потоков

Возврат освящённых объектов из потоков не работает. В зависимости от вовлечённых классов, вы можете обойти это, вернув сериализованную версию объекта (например, используя Data::Dumper или Storable), а затем восстановив его в присоединяющемся потоке. Если вы используете Perl 5.10.0 или более позднюю версию и если класс поддерживает объекты, общие для потоков, вы можете передавать их через общие очереди.

Блоки END в потоках

Можно добавить блоки END в потоки, используя require или eval с соответствующим кодом. Эти END блоки затем будут выполнены при уничтожении интерпретатора потока (т.е., либо во время вызова ->join(), либо при завершении программы).

Однако вызов любых методов threads в таком END блоке, скорее всего, не удастся (например, приложение может зависнуть или сгенерировать ошибку) из-за мьютексов, которые необходимы для управления функциями внутри модуля threads.

По этой причине использование END блоков в потоках сильно не рекомендуется.

Открытые дескрипторы каталогов

В perl 5.14 и выше, на системах, отличных от Windows, которые не поддерживают функцию fchdir C, дескрипторы каталогов (см. 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

threads::shared, perlthrtut

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.28.3/threads

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API