Perl - Thread it right!

Post date: Apr 4, 2013 10:12:07 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...

$