What? A blog?‎ > ‎

Perl - Thread it right!

posted Apr 4, 2013, 3:12 PM by Daniel Gomes   [ updated Apr 4, 2013, 3:25 PM ]
Ok, let's talk about Perl Threads.

I've seen so many dangerous scripts out there and sometimes I have to re-write them from scratch. People don't know SysV signaling and implements all sorts of crazy "technical gimmicks" causing potential (sometimes real) system instability.

If you're clueless about Perl Threads, well... start here: http://perldoc.perl.org/threads.html


Care for a "safe" Perl Threaded script? Check the skeleton below, I spent a couple of hours from my afternoon to produce a clean template. Just a few code comments so it doesn't get polluted:

-------->8-------- code snippet
-------->8--------

#!/usr/bin/perl -w

use strict;
use warnings;
use threads 'exit' => 'threads_only';

# This global variable is used only within threads
my $thread_id;


# This piece of code is executed only in the threaded process

# Signal handler for thread
sub thread_stop {
    print "Stopping thread " . $thread_id . "\n";
   
    $thread_id = "stop";
}

# Thread entry point - this is the worker sub
sub start_thread {

    # 1st thing we do is to bind the handler to the signal
    $SIG{'INT'}='thread_stop';
   
    $thread_id = $_[0];
   
    # Notice that each thread has its own $thread_id (refer to Perl
    # documentation about
interpreter threads
    # The thread scope is NOT shared by default
    while ($thread_id ne "stop") {
        # THREAD WORK! Done here!!
        print('Thread ' . $thread_id . ' - ' . localtime() . "\n");
        sleep(1);
    }
   
    return (0);
}

# From this point below...
# This piece of code is executed only in the parent process

# Signal handler for the parent process
sub main_handler {
   
    print "Finishing threads...\n";
    foreach my $thread (threads->list(threads::running)) {
        $thread->kill('INT');
    }
}

# Bind SysV signals for the parent - notice the handler (sub), different from the thread
$SIG{'TERM'}='main_handler';
$SIG{'INT'}='main_handler';

# Create threads and they'll start running afterwards
foreach my $tid ("one", "two", "three", "four") {
    threads->create('start_thread', $tid);
}

# At this point, threads are running in parallel

do {
    sleep (1);

    # Joining threads is good to have a clean flow
    # But joining is really useful only when the thread
    # is supposed to return a value
    my @joinable = threads->list(threads::joinable);
    foreach my $thread (@joinable) {
        print "Joining threads...\n";
        $thread->join();
    }
} while (threads->list());

print "Exiting...\n";

exit (0);


-------->8-------- code snippet -------->8--------

Execute the script above. It will start to print timestamps. To make it to stop just hit CTRL-C.

Sample output:

$ ./thread_test.pl
Thread one - Thu Apr  4 17:06:02 2013
Thread two - Thu Apr  4 17:06:02 2013
Thread three - Thu Apr  4 17:06:02 2013
Thread four - Thu Apr  4 17:06:02 2013
Thread one - Thu Apr  4 17:06:03 2013
Thread two - Thu Apr  4 17:06:03 2013
Thread three - Thu Apr  4 17:06:03 2013
Thread four - Thu Apr  4 17:06:03 2013
Thread one - Thu Apr  4 17:06:04 2013
Thread two - Thu Apr  4 17:06:04 2013
Thread three - Thu Apr  4 17:06:04 2013
Thread four - Thu Apr  4 17:06:04 2013
^CFinishing threads...
Stopping thread one
Stopping thread two
Stopping thread three
Stopping thread four
Joining threads...
Joining threads...
Joining threads...
Joining threads...
Exiting...
$


ċ
thread_test.pl
(2k)
Daniel Gomes,
Apr 4, 2013, 3:25 PM
Ċ
Daniel Gomes,
Apr 4, 2013, 3:12 PM
Comments