Perl: Threading with shared multi-dimensional hash

2019-02-09 07:24发布

问题:

I am trying to share a multi-dimensional hash over multiple threads. This hash holds 2 connected key-pairs, I need to know if they are already connected, if they are not, I need to connect them, if not, there is no need to go to the database.

use threads;
use threads::shared;

my %FLUobject2param : shared    = ();

#Start a new thread for every available processor
for (my $i=0;$i<$PROCESSORS;$i++) {
    threads->new(\&handlethread);
}
#Catch if these threads end
foreach my $onthr (threads->list()) {
    $onthr->join();
}

sub handlethread{
    ...
    if(not defined $FLUobject2param{$objectID}{$paramID}){
        $dbh->getObject2Param($objectID,$paramID);
        $FLUobject2param{$objectID}{$paramID} = 1;
    }
}

I keep getting the error Invalid value for shared scalar on the line
if(not defined $FLUobject2param{$objectID}{$paramID}){

This apparently has to do with perl's threads::shared only allowing you to share a single level of shared structure.

How would I still be able to check if this combination is already used over multiple threads ?

回答1:

Autovivification is your friend most of the time, but you have to be careful about it with shared values. Modify handlethread:

sub handlethread{
  # ...
  unless (exists $FLUobject2param{$objectID} &&
          exists $FLUobject2param{$objectID}{$paramID})
  {
      $dbh->getObject2Param($objectID,$paramID);
      $FLUobject2param{$objectID} = &share({});
      $FLUobject2param{$objectID}{$paramID} = 1;
  }
}

This is due do a documented limitation:

Shared variables can only store scalars, refs of shared variables, or refs of shared data …

The code above checks the hash keys separately to avoid autovivification, which will plant an unshared empty hash reference in $FLUobject2param{$objectID} if it doesn't exist yet.

Inside the conditional, we first build the appropriate scaffolding and then assign the value. Again, autovivification ordinarily handles this for you, but sharing forces us to be more deliberate.