views:

52

answers:

2

Now from what I understand under Perl ithreads all data is private unless explicitly shared.

I want to write a function which stores per thread state between calls. I assume that a side effect of all data being thread private by default would allow me to use a closure like this:

#!/usr/bin/perl -w

use strict;
use threads;

{ # closure to create local static variable
    my $per_thread_state = 0;

    sub foo {
        my $inc = shift;
        $per_thread_state += $inc;

        return $per_thread_state;
    }
}

my $inc = 0;

threads->create(
    sub { 
        my $inc = shift;
        my $i = $inc; 
        while (--$i) { 
            threads->yield(); 
            print threads->tid().":".foo($inc)."\n";
        }
    }, $inc
) while (++$inc < $ARGV[0]);

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

When I run it this looks like it works the way I expect, but I just want to be sure because I couldn't find any documentation which explicitly discusses doing something like this.

Could anyone point me to something official looking?

Edit

One other thing that seems strange is that the threads always seem to run in order of creation and don't interleave for some reason. For instance if I run:

./tsd.pl 100

Everything prints out perfectly in order. I'm on Ubuntu 9.04 if it matters.

+2  A: 

Provided you're running Perl 5.9.4+, this seems like a good candidate for use of the state keyword. If state is enabled, only your foo() subroutine will be able to modify the value of $per_thread_state.

Here's how:

use feature 'state';

sub foo {
    state $per_thread_state;
    my $inc = shift;
    $per_thread_state += $inc;
    return $per_thread_state;
}

Remember to enable state though (from perlsub):

Beginning with perl 5.9.4, you can declare variables with the state keyword in place of my. For that to work, though, you must have enabled that feature beforehand, either by using the feature pragma, or by using -E on one-liners.

perlsub also has a section on Persistent Private Variable with Closures. What you've done appears to be fine.

Zaid
Yeah, I want to stay compatible with older pre-5.9.4 Perls. Just to be sure the `BEGIN` is only needed if I put the closure's at the end of the file after the code for my "main" thread, correct?
Robert S. Barnes
Effectively, yes, though I would put a `BEGIN` in anyway
Zaid
+1  A: 

Your approach to creating per thread state is correct.

It often helps to think of Perl's threads as a more convenient way to deal with IPC and forked processes. Each call to threads->create(...) clones the current interpreter, along with everything in it, so each thread will get their own independent copy of foo() and $per_thread_state. When you join a thread, you can pass back a value, but everything else in the state of the thread's interpreter will be destroyed.

Your threads are running in creation order largely due to implementation details in Perl and the operating system (mainly because their individual execution time is below the operating system's shortest execution-time slice). To interleave the threads, you can use sleep rather than yield (or make the threads do some real work).

Eric Strom
The time slice thing makes sense. I added a multiply and divide in `foo` which loops 100 times and now I see much more consistent interleaving.
Robert S. Barnes
@Eric: I think you could answer this other question: http://stackoverflow.com/questions/2317507/why-do-my-perl-threads-execute-randomly-on-the-first-run-but-in-order-on-subseque
Robert S. Barnes