连接线程时Perl seg错误

时间:2015-07-07 20:59:56

标签: multithreading perl

我有类似下面的代码。我有一个主脚本调用另一个名为initial.pm的模块。 initial.pm打开与AMQP服务器(在我的案例中为RabbitMQ)的连接,并使用Net :: AMQP :: RabbitMQ库建立连接。一切正常,除非我尝试加入我的线程,我得到分段错误

我认为Net :: AMQP :: RabbitMQ不是线程安全的。但这只是主线程使用。我很确定如果您只是复制并通过以下代码,您可以重现错误。

我该如何解决?

main.pl

#!/usr/bin/perl
use Cwd qw/realpath/;
use File::Basename qw/dirname/;
use lib 'lib';
use threads;
use threads::shared;
use initial;

my @threads = ();
my $run :shared = 1;

my $init = load initial($name);

$SIG{'TERM'} = sub {
    $run = 0;
};

threads->create(\&proc1);
threads->create(\&proc2);


while($run){
    sleep(1);
    print "I am main thread\n";
}

$_->join() for threads->list();

sub proc1 {
    while($run){
        sleep(1);
        print "I am child thread 1 \n"
    }
}

sub proc2 {
    while($run){
       sleep(1);
       print "I am child thread 2 \n";
    }
}

LIB / initial.pm

package initial;

use Net::AMQP::RabbitMQ;
use Cwd qw/realpath/;
use File::Basename qw/dirname/;

my $mq;
my $stop = 0;

sub load {
    my $class = shift;
    my $self = {};
    connectq();
    bless $self,$class;
    return $self;
}

sub connectq {
    $mq = Net::AMQP::RabbitMQ->new();
    my ($host,$port,$user,$pass) = ('localhost','5672','guest','guest');
    $mq->connect($host, {
        user => $user,
        password => $pass,
        port => $port,
        timeout => 10,
    });

    $mq->channel_open(1);
    $mq->consume(1, 'logger');
}

1;

1 个答案:

答案 0 :(得分:1)

我无法直接重现您的问题,因为我没有安装库。

在非线程安全模块中“伪造”线程安全性的一种方法是将“使用”重新调整到您将使用它的位。

你看,当你启动一个线程时,它会复制程序状态加载的库和所有东西。

如果你的跑步(类似的话):

#!/usr/bin/env perl

use strict;
use warnings;

use XML::Twig;
use Data::Dumper;

sub thread1 {
    print threads->self->tid.": Includes:", Dumper \%INC,"\n";
}


#main;

print "Main includes:", Dumper \%INC,"\n";
threads -> create ( \&thread1 );

您会看到XML::Twig都加载了#!/usr/bin/env perl use strict; use warnings; use threads; use Data::Dumper; sub thread1 { require XML::Twig; XML::Twig -> import; print threads->self->tid.": Includes:", Dumper (\%INC),"\n"; } #main; print "Main includes:", Dumper (\%INC),"\n"; threads -> create ( \&thread1 ); foreach my $thr ( threads -> list ) { $thr -> join; } 。如果“加载”模块的过程导致某些状态更改(并且可以),那么您立即遇到潜在的线程安全问题。

但是,如果您改为:

fork

有效地导致模块在线程中动态加载 - 模块只出现在一个'代码实例'中,因此你不太可能被'线程安全'问题绊倒。

或者 - thread而不是$init ......可能是另一种选择。这有“略微不同”的“安全”问题。

但实在没有办法避免这种情况。即使使用共享变量,核心问题是 - 当您进行线程处理时,代码位会以不同的顺序发生。结果可能会发生各种各样的果味。共享var是确保每次检查相同变量的一种方法 - 例如分享Thread::Queue,但这可能会使事情变得更糟,因为你可能会使用不同的线程践踏相同的实例/套接字。

但是,您可以将“线程安全”问题减少到有限的范围,并使用例如{{1}}向/从“模块用户”线程传递消息。