Reputation: 377
I hope someone of you is able to help me with my problem. I tried to access a global shared array of objects during a threaded computation and always get the error "use of uninitialized value" although I can print their hashes.
In addition I can not change my objects, due to working with seqio objects from bioperl.
The following example shows my problem.
Thanks in advance.
object class:
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;
}
Test class:
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";
}
Upvotes: 4
Views: 2189
Reputation: 139471
The Bugs and Limitations section of the threads::shared documentation warns
When
share
is used on arrays, hashes, array refs or hash refs, any data they contain will be lost.[...] # 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'}));
Therefore, populate such variables after declaring them as shared. (Scalar and scalar refs are not affected by this problem.)
You lose the data in newly-created objects and set up later uninitialized-variable warnings with
for (0..2) {
my $o = obj->new();
$o->setData($_);
push @objs, share($o); # wipes out $o
}
Note another warning in the threads::shared documentation.
It is often not wise to share an object unless the class itself has been written to support sharing. For example, an object’s destructor may get called multiple times, once for each thread’s scope exit. Another danger is that the contents of hash-based objects will be lost due to the above mentioned limitation. See
examples/class.pl
(in the CPAN distribution of this module) for how to create a class that supports object sharing.
The code in obj.pm
becomes
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;
The changes are
lock
in the accessors.If you forget to remove the call to share
in the loop, you still get all the warnings. Change the loop to
foreach (0..2){
my $o = obj->new();
$o->setData($_);
push @objs, $o; # already shared!
}
to get the following output.
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
Upvotes: 2