hilbert has asked for the wisdom of the Perl Monks concerning the following question:

I am just beginning to experiment with threads. The following code works fine:
#!/usr/bin/perl -w use strict; my $argument=10; my %hash = sub_returning_hash($argument); print "a = "; print $hash{"a"}; print "\n"; print "b = "; print $hash{"b"}; print "\n"; print "c = "; print $hash{"c"}; print "\n"; sub sub_returning_hash { my $argument=shift; my $a=$argument+1 ; my $b=$argument+2 ; my $c=$argument+3 ; my %output_record = ( "a" => "$a", "b" => "$b", "c" => "$c" ); return %output_record; } # sub_returning_hash
and returns:
a = 11 b = 12 c = 13
Now I want to execute the subroutine as a thread, so I try:
#!/usr/bin/perl -w use strict; use threads; my $argument=10; my $thr = threads->new(\&sub_returning_hash, $argument); my %hash = $thr->join; print "a = "; print $hash{"a"}; print "\n"; print "b = "; print $hash{"b"}; print "\n"; print "c = "; print $hash{"c"}; print "\n"; sub sub_returning_hash { my $argument=shift; my $a=$argument+1 ; my $b=$argument+2 ; my $c=$argument+3 ; my %output_record = ( "a" => "$a", "b" => "$b", "c" => "$c" ); return %output_record; } # sub_returning_hash
but I receive:
Odd number of elements in hash assignment at test.pl line 8. Use of uninitialized value in print at test.pl line 9. a = Use of uninitialized value in print at test.pl line 10. b = Use of uninitialized value in print at test.pl line 11. c =
I tried the same returning a scalar instead of an hash, and it works fine. Any suggestion to be able to access the hash returned by the subroutine when running as a thread? Thanks!

Replies are listed 'Best First'.
Re: Threads that return hash
by BrowserUk (Patriarch) on Apr 24, 2012 at 10:55 UTC

    Your problem is that the return context (scalar/array/void) of the thread subroutine, is defined by the context in which that thread is created.

    In your example, that means here:

    my $thr = threads->new(\&sub_returning_hash, $argument);

    Which is a scalar context. Hence, when the return is invoked here:

    return %output_record;

    It is the equivalent of

    return scalar %output_record;

    Which returns:

    %h = qw[ a 11 b 12 c 13 ];; print scalar %h;; 3/8

    And when that string is returned from the join and assigned to the hash in the calling code:

    %r = scalar %h;; Odd number of elements in hash assignment

    A complicated explanation, but thankfully, the solution is simple. Change the thread creation line to:

    my( $thr ) = threads->new(\&sub_returning_hash, $argument);

    Which puts the creation of the thread into an array (list) context.

    Now the code will run warnings free and produce the desired result:

    [11:51:36.25]C:\test>junk a = 11 b = 12 c = 13

    Alternatively, you can mark the return context of the thread sub explicitly at creation time:

    my $thr = threads->new( {'context' => 'list'}, \&sub_returning_hash, $ +argument );

    which will achieve the same result. See the section labelled "THREAD CONTEXT" in the threads pod for details.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Re: Threads that return hash
by Anonymous Monk on Apr 24, 2012 at 10:48 UTC

    Its a common pitfall, the context is established here

    my $thr = threads->create(\&sub1, 100 ); # scalar my ($thr) = threads->create(\&sub1, 100 ); # list

    Explained at http://perldoc.perl.org/threads.html#THREAD-CONTEXT

    If you ask me, this is really a bug in threads::join. If the join context is different from the creation context, join should complain.

Re: Threads that return hash
by Khen1950fx (Canon) on Apr 24, 2012 at 11:32 UTC
    Following BrowserUk's advice:
    #!/usr/bin/perl use threads; use Data::Dumper::Concise; sub sub_returning_hash { use strict; no strict 'refs'; use warnings; no warnings 'uninitialized'; my $argument = shift(); my $a = $argument + 1; my $b = $argument + 2; my $c = $argument + 3; my(%output_record) = ( a => "$a", b => "$b", c => "$c", ); return %output_record; } use strict; use warnings; my $argument = 10; my($thr) = threads->create(\&sub_returning_hash, $argument); my (%hash) = $thr->join(); print Dumper( "a = $hash{'a'}" ); print Dumper( "b = $hash{'b'}" ); print Dumper( "c = $hash{'c'}" );
Re: Threads that return hash
by Anonymous Monk on Apr 24, 2012 at 12:00 UTC
    Thanks a lot to all those who answered: problem solved! This is one nice thing about Perl: a large community of skilled experts who is willing to help in specific problems. I have seen that there is a voting system in this web site, but I don't know it: do I have to vote in order to say thank?
      do I have to vote in order to say thank?

      No. You just did that :)

      But if you want to vote here, you have to become a member.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      The start of some sanity?

        Be that as it may, I just dropped 4 up-votes on this thread today, and bookmarked it on my browser.   Excellent information.   The OP’s right.

        “Contexts” (scalar, array, etc.) in Perl are one of its rather peculiar features ... full of traps sometimes for the unwary, especially when there isn’t a warning to alert you to their presence.   But the support provided by this community is excellent indeed.