Perl — Как создать библиотеку, специфичную для отдельных потоков

Вопрос:

Я пишу многопоточный скрипт в perl. В котором я использую библиотеку Net::Netconf::Manager которой inturn использует Net::SSH2. Эта Net :: SSH2 (libssh2), похоже, не является потокобезопасной при одновременном совместном использовании общих дескрипторов.

Я цитирую, как на сайте libssh2

Потоковая безопасность: просто не обменивайтесь ручками одновременно

  1. Я не уверен, что означают эти «обменные ручки». Также я хотел бы знать, как «не разделить дескрипторы».

Когда я запускаю свой скрипт, иногда я вижу трассировку ошибок с обратным трафиком и картой памяти, обозначающей *** glibc detected *** perl: double free or corruption (out): 0x00007f0320012d70 *** ошибка *** glibc detected *** perl: double free or corruption (out): 0x00007f0320012d70 ***. Эта ошибка связана с безопасностью потоков библиотеки Net :: SSh2.

  1. Как сделать этот Net :: Netconf :: Manager доступным для каждого потока, а не объявить его глобально с помощью » use «. Я хочу, чтобы все потоки имели свой собственный доступ к этой библиотеке независимо от других потоков.

Пожалуйста, дайте мне знать ваши взгляды.

Лучший ответ:

Я текущий поддерживающий Net::SSH2.

Я никогда не занимался безопасностью потока для этого модуля, но неглубоко проверял его код, показывает, что, вероятно, эта двойная свободная ошибка вызвана стороной Perl объектов Net::SSH2, клонированных при создании потоков, в то время как сторона C не является, Это приводит к libssh2 объекты libssh2 уничтожаются и выпускаются дважды, что приводит к сбою программы.

Итак, если вы хотите использовать Net::SSH2 в многопоточном приложении, вы должны убедиться, что потоки никогда не создаются из потоков, где существуют объекты этого модуля.

Даже тогда могут быть другие ошибки, скрывающиеся в модуле.

Очевидно, что правильная вещь — это исправить модуль. Если вы хотите сделать это самостоятельно, я постараюсь вам помочь. Просто свяжитесь со мной, чтобы мы могли сначала обсудить детали… В противном случае, теперь, когда вы привлекли эту проблему к моему вниманию, ну, возможно, в какой-то момент я сам ее исправлю… но это не будет произойти в одночасье.

Ответ №1

Общим обходным решением проблем «потоковой безопасности» является «требуется» и «импорт». Они должны быть вызваны после создания какого-либо потокового создания экземпляра, и никакие потоки не могут быть созданы после (откуда бы ни загружались модули — это нормально внутри «main»).

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

#!/usr/bin/env perl

use strict;
use warnings;
use threads;
use Thread::Queue;

my $num_workers = 10;

my %generalargs = (
'access'              => 'ssh',
'server'              => 'netconf',
'command'             => 'junoscript netconf',
'debug_level'         => 1,
'client_capabilities' => [
'urn:ietf:params:xml:ns:netconf:base:1.0',
'urn:ietf:params:xml:ns:netconf:capability:candidate:1.0',
'urn:ietf:params:xml:ns:netconf:capability:confirmed-commit:1.0',
'urn:ietf:params:xml:ns:netconf:capability:validate:1.0',
'urn:ietf:params:xml:ns:netconf:capability:url:1.0?protocol=http,ftp,file',
'http://xml.juniper.net/netconf/junos/1.0',
]
);

my @host_list = (
{  'hostname' => 'routername',
'login'    => 'loginname',
'password' => 'secret',
},
{  'hostname' => 'routername2',
'login'    => 'differentname',
'password' => 'anotherpassword',
},
);

my $work_q = Thread::Queue->new;


sub some_helper_sub_that_isnt_a_thread {
my ( $input, $process ) = @_;
return "$input";
}

sub do_netconf_stuff {
require 'Net::NetConf::Manager';
Net::NetConf::Manager->import;

while ( my $item = work_q->dequeue ) {
my $device = Net::NetConf::Manager->new( %{$item} );
print 'Could not create Netconf device' unless $device;
some_helper_sub($device);
}
}

threads->create( \&do_netconf_stuff ) for 1 .. $num_workers;

foreach my $host (@host_list) {
$work_q->enqueue( { %$host, %generalargs } );
}
$work_q->end;

$_->join for threads->list;

Что происходит здесь, так это то, что каждый поток независимо — и во время выполнения — импортирует Net::NetConf::Manager и это означает, что каждый из них Net::NetConf::Manager отдельно. Затем вы можете вызывать другие субтитры из потока, и они будут работать нормально — вы загрузили в глобальное пространство имен для этого потока.

То, что вы не должны делать, это запустить дополнительные потоки, которые будут «наследовать» импортированную среду.

Примечание. Это не работает на 100% — есть другие причины, по которым потоки могут столкнуться (например, пытаться прослушивать один и тот же номер порта, блокировать одни и те же файлы и т.д.). Но вы избежите проблем в модулях из-за обмена файлами и т.д.

Оцените статью
TechArks.Ru
Добавить комментарий