threads::shared
СОДЕРЖАНИЕ
- ИМЯ
- ВЕРСИЯ
- СИНОПСИС
- ОПИСАНИЕ
- ЭКСПОРТ
- ФУНКЦИИ
- ОБЪЕКТЫ
- ПРИМЕЧАНИЯ
- ПРЕДУПРЕЖДЕНИЯ
- ОШИБКИ И ОГРАНИЧЕНИЯ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- ЛИЦЕНЗИЯ
ИМЯ
threads::shared — Perl-расширение для совместного использования структур данных между потоками
ВЕРСИЯ
В данном документе описана версия threads::shared 1.62
СИНОПСИС
use threads;
use threads::shared;
my $var :shared;
my %hsh :shared;
my @ary :shared;
my ($scalar, @array, %hash);
share($scalar);
share(@array);
share(%hash);
$var = $scalar_value;
$var = $shared_ref_value;
$var = shared_clone($non_shared_ref_value);
$var = shared_clone({'foo' => [qw/foo bar baz/]});
$hsh{'foo'} = $scalar_value;
$hsh{'bar'} = $shared_ref_value;
$hsh{'baz'} = shared_clone($non_shared_ref_value);
$hsh{'quz'} = shared_clone([1..3]);
$ary[0] = $scalar_value;
$ary[1] = $shared_ref_value;
$ary[2] = shared_clone($non_shared_ref_value);
$ary[3] = shared_clone([ {}, [] ]);
{ lock(%hash); ... }
cond_wait($scalar);
cond_timedwait($scalar, time() + 30);
cond_broadcast(@array);
cond_signal(%hash);
my $lockvar :shared;
# condition var != lock var
cond_wait($var, $lockvar);
cond_timedwait($var, time()+30, $lockvar); ОПИСАНИЕ
По умолчанию переменные приватны для каждого потока, и каждый новый поток получает собственную копию каждой существующей переменной. Этот модуль позволяет совместно использовать переменные между различными потоками (и псевдо-вилками на Win32). Он используется совместно с модулем threads.
Этот модуль поддерживает совместное использование только следующих типов данных: скаляры и ссылки на скаляры, массивы и ссылки на массивы, и хэши и ссылки на хэши.
ЭКСПОРТ
Следующие функции экспортируются этим модулем: share, shared_clone, is_shared, cond_wait, cond_timedwait, cond_signal и cond_broadcast
Обратите внимание, что если этот модуль импортирован, когда threads еще не загружен, то все эти функции становятся бесполезными. Это позволяет создавать модули, которые будут работать как в потоковых, так и в непотоковых средах.
ФУНКЦИИ
-
shareпринимает переменную и помечает её как общую:my ($scalar, @array, %hash); share($scalar); share(@array); share(%hash);shareвернёт общую правую часть, но всегда как ссылку.Переменные также могут быть помечены как общие во время компиляции, используя атрибут
:shared:my ($var, %hash, @array) :shared;Общие переменные могут содержать только скаляры, ссылки на общие переменные или ссылки на общие данные (обсуждаются в следующем разделе):
my ($var, %hash, @array) :shared; my $bork; # Storing scalars $var = 1; $hash{'foo'} = 'bar'; $array[0] = 1.5; # Storing shared refs $var = \%hash; $hash{'ary'} = \@array; $array[1] = \$var; # The following are errors: # $var = \$bork; # ref of non-shared variable # $hash{'bork'} = []; # non-shared array ref # push(@array, { 'x' => 1 }); # non-shared hash ref -
shared_cloneпринимает ссылку и возвращает общую версию её аргумента, выполняя глубокую копию любых неопределённых элементов. Любые общие элементы в аргументе используются как есть (т.е. они не клонируются).my $cpy = shared_clone({'foo' => [qw/foo bar baz/]});Состояние объекта (т.е. класс, в который благословлён объект) также клонируется.
my $obj = {'foo' => [qw/foo bar baz/]}; bless($obj, 'Foo'); my $cpy = shared_clone($obj); print(ref($cpy), "\n"); # Outputs 'Foo'Для клонирования пустых ссылок на массивы или хэши также можно использовать:
$var = &share([]); # Same as $var = shared_clone([]); $var = &share({}); # Same as $var = shared_clone({});Не все типы данных Perl могут быть клонированы (например, globs, ссылки на код). По умолчанию
shared_cloneбудет croak, если встретит такие элементы. Чтобы изменить это поведение на предупреждение, задайте следующее:$threads::shared::clone_warn = 1;В этом случае
undefбудет заменено на элемент, подлежащий клонированию. Если задано значение ноль:$threads::shared::clone_warn = 0;то замена
undefбудет выполнена безмолвно. -
is_sharedпроверяет, является ли указанная переменная общей или нет. Если общая, возвращает внутренний идентификатор переменной (аналогичныйrefaddr()(см. Scalar::Util). В противном случае возвращаетundef.if (is_shared($var)) { print("\$var is shared\n"); } else { print("\$var is not shared\n"); }При использовании с элементом массива или хэша
is_sharedпроверяет, принадлежит ли указанный элемент общему массиву или хэшу. (Он не проверяет содержимое этого элемента.)my %hash :shared; if (is_shared(%hash)) { print("\%hash is shared\n"); } $hash{'elem'} = 1; if (is_shared($hash{'elem'})) { print("\$hash{'elem'} is in a shared hash\n"); } - lock VARIABLE
-
lockнакладывает консультационный замок на переменную до тех пор, пока замок не выйдет из области видимости. Если переменная заблокирована другим потоком, вызовlockбудет блокировать выполнение до тех пор, пока она не станет доступна. Многократные вызовыlockодним и тем же потоком из динамически вложенных областей видимости безопасны — переменная останется заблокированной до тех пор, пока внешний замок на переменной не выйдет из области видимости.lockследует за ссылками ровно на один уровень:my %hash :shared; my $ref = \%hash; lock($ref); # This is equivalent to lock(%hash)Обратите внимание, что вы не можете явно разблокировать переменную; вы можете только дождаться, пока замок выйдет из области видимости. Это проще всего достигается путём блокировки переменной внутри блока.
my $var :shared; { lock($var); # $var is locked from here to the end of the block ... } # $var is now unlockedПоскольку замки являются консультативными, они не препятствуют доступу или изменению данных другим потоком, который сам не пытается получить замок на переменной.
Вы не можете заблокировать отдельные элементы контейнерной переменной:
my %hash :shared; $hash{'foo'} = 'bar'; #lock($hash{'foo'}); # Error lock(%hash); # WorksЕсли вам нужен более точный контроль над доступом к общим переменным, см. Thread::Semaphore.
- cond_wait VARIABLE
- cond_wait CONDVAR, LOCKVAR
-
Функция
cond_waitпринимает заблокированную переменную в качестве параметра, разблокирует переменную и блокирует выполнение до тех пор, пока другой поток не выполнитcond_signalилиcond_broadcastдля той же заблокированной переменной. Переменная, по которойcond_waitблокировалась, снова блокируется после того, какcond_waitвыполнится. Если существует несколько потоковcond_waitпо одной переменной, все кроме одного будут снова блокироваться, ожидая повторного получения блокировки на переменной. (Поэтому, если вы используетеcond_waitтолько для синхронизации, отдайте блокировку как можно быстрее). Два действия разблокировки переменной и входа в состояние ожидания блокировки атомны, но два действия выхода из состояния ожидания блокировки и повторной блокировки переменной атомны.Во второй форме
cond_waitпринимает общую, незаблокированную переменную, за которой следует общая, заблокированная переменная. Вторая переменная разблокируется, и выполнение потока приостанавливается до тех пор, пока другой поток не сообщит о первой переменной.Важно отметить, что переменная может быть уведомлена даже если ни один поток
cond_signalилиcond_broadcastпо переменной. Поэтому важно проверить значение переменной и вернуться к ожиданию, если требование не выполнено. Например, чтобы приостановить выполнение, пока общий счётчик не опустится до нуля:{ lock($counter); cond_wait($counter) until $counter == 0; } - cond_timedwait VARIABLE, ABS_TIMEOUT
- cond_timedwait CONDVAR, ABS_TIMEOUT, LOCKVAR
-
В форме с двумя аргументами
cond_timedwaitпринимает заблокированную переменную и абсолютный таймаут в эпохальных секундах (см. time() в perlfunc для более подробной информации) в качестве параметров, разблокирует переменную и блокирует выполнение до достижения таймаута или сигнала переменной другим потоком. Ложное значение возвращается, если таймаут достигнут, и истинное значение в противном случае. В любом случае переменная повторно блокируется по возвращении.Как и
cond_wait, эта функция может принять общую, заблокированную переменную в качестве дополнительного параметра; в этом случае первый параметр является незаблокированной переменной состояния, защищённой отдельной переменной блокировки.Опять же, как и
cond_wait, пробуждение и повторное получение блокировки не являются атомными, и вы всегда должны проверять желаемое условие после возврата из этой функции. Поскольку таймаут является абсолютным значением, его не нужно пересчитывать на каждом проходе:lock($var); my $abs = time() + 15; until ($ok = desired_condition($var)) { last if !cond_timedwait($var, $abs); } # we got it if $ok, otherwise we timed out! - cond_signal VARIABLE
-
Функция
cond_signalпринимает заблокированную переменную в качестве параметра и разблокирует один поток, которыйcond_waitпо этой переменной. Если более одного потока заблокированы вcond_waitпо этой переменной, только один (и какой — неизвестно) будет разблокирован.Если потоки, заблокированные в
cond_waitпо переменной, отсутствуют, сигнал отбрасывается. Постоянное блокирование перед сигнализацией позволяет (при соблюдении осторожности) избежать сигнализации до того, как другой поток войдёт в cond_wait().cond_signalобычно вызовет предупреждение, если вы попытаетесь использовать его с незаблокированной переменной. В редких случаях, когда это может быть уместно, вы можете подавить предупреждение, используя:{ no warnings 'threads'; cond_signal($foo); } - cond_broadcast VARIABLE
-
Функция
cond_broadcastработает аналогичноcond_signal.cond_broadcast, однако, разблокирует все потоки, которые заблокированы вcond_waitпо заблокированной переменной, а не только один.
ОБЪЕКТЫ
threads::shared экспортирует версию bless(), которая работает с общими объектами таким образом, что благословения распространяются через потоки.
# Create a shared 'Foo' object
my $foo :shared = shared_clone({});
bless($foo, 'Foo');
# Create a shared 'Bar' object
my $bar :shared = shared_clone({});
bless($bar, 'Bar');
# Put 'bar' inside 'foo'
$foo->{'bar'} = $bar;
# Rebless the objects via a thread
threads->create(sub {
# Rebless the outer object
bless($foo, 'Yin');
# Cannot directly rebless the inner object
#bless($foo->{'bar'}, 'Yang');
# Retrieve and rebless the inner object
my $obj = $foo->{'bar'};
bless($obj, 'Yang');
$foo->{'bar'} = $obj;
})->join();
print(ref($foo), "\n"); # Prints 'Yin'
print(ref($foo->{'bar'}), "\n"); # Prints 'Yang'
print(ref($bar), "\n"); # Also prints 'Yang' ПРИМЕЧАНИЯ
threads::shared разработан для того, чтобы безмолвно отключить себя, если потоки недоступны. Это позволяет вам создавать модули и пакеты, которые могут использоваться в потоковых и непотоковых приложениях.
Если вы хотите получить доступ к потокам, вы должны use threads до того, как use threads::shared. threads выведет предупреждение, если вы его используете после threads::shared.
ПРЕДУПРЕЖДЕНИЯ
- cond_broadcast() вызван для незаблокированной переменной
- cond_signal() вызван для незаблокированной переменной
-
См. "cond_signal VARIABLE" выше.
ОШИБКИ И ОГРАНИЧЕНИЯ
Когда share используется с массивами, хэшами, ссылками на массивы или ссылки на хэши, все данные в них будут потеряны.
my @arr = qw(foo bar baz);
share(@arr);
# @arr is now empty (i.e., == ());
# Create a 'foo' object
my $foo = { 'data' => 99 };
bless($foo, 'foo');
# Share the object
share($foo); # Contents are now wiped out
print("ERROR: \$foo is empty\n")
if (! exists($foo->{'data'})); Поэтому заполняйте такие переменные после их объявления как общих. (Скаляры и ссылки на скаляры не затрагиваются этой проблемой.)
Благословение общего элемента после того, как он был вложен в другой общий элемент, не распространяет благословение на общую ссылку:
my $foo = &share({});
my $bar = &share({});
$bar->{foo} = $foo;
bless($foo, 'baz'); # $foo is now of class 'baz',
# but $bar->{foo} is unblessed. Поэтому вы должны благословить объекты до того, как поделиться ими.
Часто неразумно разделять объект, если сам класс не написан с поддержкой совместного использования. Например, деструктор общего объекта может быть вызван несколько раз, один раз для выхода из области видимости каждого потока, или вообще не вызываться, если он вложен в другой общий объект. Ещё одна проблема в том, что содержимое хэш-объектов будет потеряно из-за вышеупомянутого ограничения. См. examples/class.pl (в CPAN-распространении этого модуля) для того, как создать класс, поддерживающий совместное использование объектов.
Деструкторы могут не вызываться для объектов, если эти объекты всё ещё существуют во время глобального уничтожения. Если деструкторы должны быть вызваны, убедитесь, что нет циклических ссылок и что ничего не ссылается на объекты до завершения программы.
Не поддерживает splice для массивов. Не поддерживает явное изменение длины массива через $#array — используйте push и pop вместо этого.
Получение ссылок на элементы общих массивов и хешей не приводит к автоматическому созданию элементов, и срез общего массива/хеша по несуществующим индексам/ключам также не приводит к автоматическому созданию элементов.
share() позволяет вам share($hashref->{key}) и share($arrayref->[idx]) без сообщений об ошибках. Но $hashref->{key} или $arrayref->[idx] не являются общими, что приводит к ошибке «замок может быть использован только для общих значений», когда вы пытаетесь lock($hashref->{key}) или lock($arrayref->[idx]) в другом потоке.
Использование refaddr() ненадёжно для проверки эквивалентности двух общих ссылок (например, при проверке на наличие циклических ссылок). Используйте is_shared() вместо этого:
use threads;
use threads::shared;
use Scalar::Util qw(refaddr);
# If ref is shared, use threads::shared's internal ID.
# Otherwise, use refaddr().
my $addr1 = is_shared($ref1) || refaddr($ref1);
my $addr2 = is_shared($ref2) || refaddr($ref2);
if ($addr1 == $addr2) {
# The refs are equivalent
} each() не работает должным образом с общими ссылками, встроенными в общие структуры. Например:
my %foo :shared;
$foo{'bar'} = shared_clone({'a'=>'x', 'b'=>'y', 'c'=>'z'});
while (my ($key, $val) = each(%{$foo{'bar'}})) {
...
} Вместо этого можно использовать любое из следующих решений:
my $ref = $foo{'bar'};
while (my ($key, $val) = each(%{$ref})) {
...
}
foreach my $key (keys(%{$foo{'bar'}})) {
my $val = $foo{'bar'}{$key};
...
} Этот модуль поддерживает переменные с двойным значением, созданные с помощью dualvar() из Scalar::Util. Однако, хотя $! ведет себя как dualvar, она реализована как привязанный SV. Для распространения её значения используйте следующий конструкт, если это необходимо:
my $errno :shared = dualvar($!,$!); Просмотреть существующие сообщения об ошибках и отправить любые новые сообщения об ошибках, проблемы, исправления и т. д.: http://rt.cpan.org/Public/Dist/Display.html?Name=threads-shared
СМОТРИТЕ ТАКЖЕ
threads::shared на MetaCPAN: https://metacpan.org/release/threads-shared
Репозиторий кода для CPAN-распределения: https://github.com/Dual-Life/threads-shared
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
Пример кода в каталоге examples этого распределения на CPAN.
АВТОР
Artur Bergman <sky AT crucially DOT net>
Документация взята из старого Thread.pm.
Версия CPAN, подготовленная Jerry D. Hedden <jdhedden AT cpan DOT org>.
ЛИЦЕНЗИЯ
threads::shared распространяется под той же лицензией, что и Perl.
© 1993–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.34.0/threads::shared