Perl - 如何制作特定于各个线程的库
Perl - How to make a library specific to individual threads
我正在用 perl 编写多线程脚本。我在其中使用了一个库 Net::Netconf::Manager
,它又使用了 Net::SSH2
。 Net::SSH2(libssh2) 在同时 'shared handles' 时似乎不是线程安全的。
我引用 libssh2 website
线程安全:只是不要同时共享句柄
- 我不确定 "sharing handles" 是什么意思。我也想知道如何 'not share handles'.
当我 运行 我的脚本时,偶尔我会看到带有回溯和内存映射的错误跟踪,表示 *** glibc detected *** perl: double free or corruption (out): 0x00007f0320012d70 ***
错误。这个错误是因为 Net::SSh2 库的线程安全性。
- 如何使这个 Net::Netconf::Manager 对每个线程都可用,而不是用 '
use
' 全局声明它。我希望所有线程都可以独立于其他线程访问该库。
请告诉我你的看法。
我是 Net::SSH2
的当前维护者。
我从未追求该模块的线程安全性,但对其代码的粗略检查表明,double free 错误可能是由 Net::SSH2
对象的 Perl 端在线程创建时被克隆引起的而C端则不是。这导致 libssh2
对象被销毁和释放两次,导致程序崩溃。
因此,如果您想在多线程应用程序中使用 Net::SSH2
,您应该确保永远不会从存在此模块对象的线程创建线程。
即便如此,模块上也可能潜伏着其他错误。
显然正确的做法是修复模块。如果你想自己做,我会尽力帮助你。请与我联系,以便我们可以先讨论细节......否则,既然你已将这个问题提请我注意,好吧,也许在某个时候我会自己解决它......但这不会一夜之间发生。
'thread safety' 问题的一般解决方法是 'require' 和 'import'。这些必须在 在 发生任何类型的线程实例化之后调用,并且之后不能创建线程(从加载模块的任何地方 - 在 '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
这意味着它们分别被实例化。然后您可以从该线程中调用其他子程序,它们会正常工作 - 您已经加载到该线程的全局命名空间 。
您不能做的事情就是启动额外的线程,这些线程将 'inherit' 导入的环境。
注意 - 这并非 100% 肯定有效 - 线程可能会发生冲突还有其他原因(例如尝试侦听相同的端口号、锁定相同的文件等)。但是由于共享文件句柄等,您将避免模块内的问题。
我正在用 perl 编写多线程脚本。我在其中使用了一个库 Net::Netconf::Manager
,它又使用了 Net::SSH2
。 Net::SSH2(libssh2) 在同时 'shared handles' 时似乎不是线程安全的。
我引用 libssh2 website
线程安全:只是不要同时共享句柄
- 我不确定 "sharing handles" 是什么意思。我也想知道如何 'not share handles'.
当我 运行 我的脚本时,偶尔我会看到带有回溯和内存映射的错误跟踪,表示 *** glibc detected *** perl: double free or corruption (out): 0x00007f0320012d70 ***
错误。这个错误是因为 Net::SSh2 库的线程安全性。
- 如何使这个 Net::Netconf::Manager 对每个线程都可用,而不是用 '
use
' 全局声明它。我希望所有线程都可以独立于其他线程访问该库。
请告诉我你的看法。
我是 Net::SSH2
的当前维护者。
我从未追求该模块的线程安全性,但对其代码的粗略检查表明,double free 错误可能是由 Net::SSH2
对象的 Perl 端在线程创建时被克隆引起的而C端则不是。这导致 libssh2
对象被销毁和释放两次,导致程序崩溃。
因此,如果您想在多线程应用程序中使用 Net::SSH2
,您应该确保永远不会从存在此模块对象的线程创建线程。
即便如此,模块上也可能潜伏着其他错误。
显然正确的做法是修复模块。如果你想自己做,我会尽力帮助你。请与我联系,以便我们可以先讨论细节......否则,既然你已将这个问题提请我注意,好吧,也许在某个时候我会自己解决它......但这不会一夜之间发生。
'thread safety' 问题的一般解决方法是 'require' 和 'import'。这些必须在 在 发生任何类型的线程实例化之后调用,并且之后不能创建线程(从加载模块的任何地方 - 在 '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
这意味着它们分别被实例化。然后您可以从该线程中调用其他子程序,它们会正常工作 - 您已经加载到该线程的全局命名空间 。
您不能做的事情就是启动额外的线程,这些线程将 'inherit' 导入的环境。
注意 - 这并非 100% 肯定有效 - 线程可能会发生冲突还有其他原因(例如尝试侦听相同的端口号、锁定相同的文件等)。但是由于共享文件句柄等,您将避免模块内的问题。