4

我希望你们中的某个人能够帮助我解决我的问题。我试图在线程计算期间访问对象的全局共享数组,并且总是得到错误“使用未初始化的值”,尽管我可以打印它们的哈希值。

此外,由于使用来自 bioperl 的 seqio 对象,我无法更改我的对象。

以下示例显示了我的问题。

提前致谢。

对象类:

package obj;

use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);

sub new(){
    my $class=shift;
    my $this = {};
    $this->{"data"} = ();   
    bless($this,$class);
    return($this);
}

sub getData(){
    my $this=shift;
    return $this->{"data"};
}

sub setData($){
    my $this=shift; 
    $this->{"data"}=shift;
}

测试类:

use strict;
use warnings;
use threads;
use threads::shared;
use obj;

my @objs : shared;
foreach (0..2){
    my $o = obj->new();
    $o->setData($_);
    push @objs, share($o);  
}

my @threads=();
$#threads=3;

my $started;
for (my $i=0; $i<10; $i+=$started){
    $started=0;
    foreach (0..$#threads){
        if (not defined $threads[$_]){
            $threads[$_]=threads->new(\&run,(\@objs));
            $started++; 
        } elsif($threads[$_]->is_joinable()){
            $threads[$_]->join();
            $threads[$_]=threads->new(\&run,(\@objs));
            $started++;
        }           
    }   
}
my $running=1;
while ($running>0) {
    foreach (@threads) {    
        if (defined $_){
            $_->join if ($_->is_joinable());    
        }               
    }
    $running = scalar(threads->list(threads::running));       
}   

sub run($){
    my $objs=shift;

    print $_." " foreach (@{$objs});
#   print $_->getData()." " foreach (@{$objs}); try to access data

    print "\n";     
}
4

1 回答 1

2

线程::共享文档的错误和限制部分警告

share用于数组、哈希、数组引用或哈希引用时,它们包含的任何数据都将丢失。

[...]

# 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'}));

因此,在将这些变量声明为共享填充这些变量。(标量和标量引用不受此问题的影响。)

您丢失了新创建的对象中的数据,并设置了以后的未初始化变量警告

for (0..2) {
    my $o = obj->new();
    $o->setData($_);
    push @objs, share($o);  # wipes out $o
}

请注意threads::shared 文档中的另一个警告。

除非类本身已被编写为支持共享,否则共享对象通常是不明智的。例如,一个对象的析构函数可能会被调用多次,每个线程的作用域退出一次。另一个危险是,由于上述限制,基于散列的对象的内容将丢失。请参阅examples/class.pl(在本模块的 CPAN 分发版中)了解如何创建支持对象共享的类。

中的代码obj.pm变为

package obj;

use strict;
use threads;
use threads::shared;
use warnings;

sub new {
    my $class=shift;
    share(my %this);
    $this{"data"} = ();
    bless(\%this,$class);
    return(\%this);
}

sub getData {
    my $this=shift;
    lock($this);
    return $this->{"data"};
}

sub setData {
    my $this=shift;
    lock($this);
    $this->{"data"}=shift;
}

1;

变化是

  • 使用threads 和threads::shared 模块。
  • 删除未使用的出口商咒语。
  • 在构造函数中,创建一个空的共享哈希,然后初始化并祝福。
  • 在访问器中添加调用lock

share如果您忘记删除循环中的调用,您仍然会收到所有警告。将循环更改为

foreach (0..2){
    my $o = obj->new();
    $o->setData($_);
    push @objs, $o;  # already shared!
}

得到以下输出。

0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
0 1 2
于 2012-12-01T15:47:25.867 回答