2

我正在用 perl 编写一个多线程脚本。我在其中使用了一个库,而该库Net::Netconf::Manager又使用Net::SSH2. 当同时“共享句柄”时,这个 Net::SSH2(libssh2) 似乎不是线程安全的。

我在libssh2 网站中引用

线程安全:只是不要同时共享句柄

  1. 我不确定这个“共享句柄”是什么意思。另外我想知道如何“不共享句柄”。

当我运行我的脚本时,有时我会看到带有回溯的错误跟踪和表示*** glibc detected *** perl: double free or corruption (out): 0x00007f0320012d70 ***错误的内存映射。这个错误是因为 Net::SSh2 库的线程安全。

  1. 如何使这个 Net::Netconf::Manager 可用于每个线程,而不是用 ' use' 全局声明它。我希望所有线程都可以独立于其他线程来访问这个库。

请让我知道你的看法。

4

2 回答 2

3

“线程安全”问题的一般解决方法是“要求”和“导入”。这些必须在发生任何类型的线程实例化之后调用,并且之后不能创建线程(从加载模块的任何位置 - 在“主”中都可以)。

所以 - 因为你没有给我们任何代码,我已经使用了模块之外的示例。您需要进行相应的修改。

#!/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并且这意味着它们每个都被单独实例化。然后,您可以从线程内调用其他子程序,它们会正常工作 - 您已加载到该线程的全局命名空间中。

然后你不能做的事情是启动额外的线程来“继承”那个导入的环境。

注意 - 这不是 100% 肯定会工作 - 线程可能会发生冲突还有其他原因(例如尝试侦听相同的端口号、锁定相同的文件等)。但是由于共享文件句柄等,您将避免模块内的问题。

于 2017-11-06T14:59:15.270 回答
3

我是当前的维护者Net::SSH2

我从未追求过该模块的线程安全性,但对其代码的浅显检查表明,双重释放错误可能是由在Net::SSH2创建线程时克隆的对象的 Perl 端引起的,而 C 端不是。这会导致libssh2对象被销毁和释放两次,从而导致程序崩溃。

因此,如果您想Net::SSH2在多线程应用程序中使用,您应该确保永远不会从存在此模块对象的线程中创建线程。

即使这样,模块上也可能潜伏着其他错误。

显然,正确的做法是修复模块。如果你想自己做,我会尽力帮助你。请与我联系,以便我们先讨论细节...否则,既然您已经将这个问题引起了我的注意,好吧,也许在某个时候我会自己解决它...但这不会一夜之间发生。

于 2017-11-05T08:20:51.667 回答