threads::shared
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- СИНТЕКС
- ОПИСАНИЕ
- ЭКСПОРТ
- ФУНКЦИИ
- ОБЪЕКТЫ
- ПРИМЕЧАНИЯ
- ПРЕДУПРЕЖДЕНИЯ
- ОШИБКИ И ОГРАНИЧЕНИЯ
- СМОТРИТЕ ТАКЖЕ
- АВТОР
- ЛИЦЕНЗИЯ
НАЗВАНИЕ
threads::shared - Расширение Perl для совместного использования структур данных между потоками
ВЕРСИЯ
В этом документе описывается версия threads::shared 1.64
СИНТЕКС
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 могут быть клонированы (например, глобы, ссылки на код). По умолчанию,
shared_cloneвызовет croak, если встретит такие элементы. Чтобы изменить это поведение на предупреждение, установите следующее:$threads::shared::clone_warn = 1;В этом случае,
undefбудет заменено на элемент, который нужно клонировать. Если значение равно нулю:$threads::shared::clone_warn = 0;то замена
undefбудет выполнена молча. -
is_sharedпроверяет, является ли указанная переменная общей или нет. Если общая, возвращает внутренний идентификатор переменной (аналогичноrefaddr()(см. Scalar::Util). В противном случае возвращаетundefif (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.36.0/threads::shared