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

I need a thread pool. Something that would allow me to something like this.
$pool = Pool::Of::Threads->new(how_many_threads => 5); for (1 .. 1000) { $pool->job({sleep 5; print ($_: I'm in the thread}); }
As the loop runs each call to job goes to a different thread until 5 threads are used, and then the loop waits until a thread becomes available. When a thread is available the loop uses it etc...

I'd like to have the threads started in the call to new and then reused so that I don't have the cost of creating every time. I've seen Thread::Pool and it look perfect, but I can't get it to install, and when I force install scripts fail in many ways. Has anyone gotten this to work? I'd like to do any of the following:

Get Thread::Pool to work.
Find another solution that does the same.
Roll my own.

Looking into rolling my own, I can see how most of the steps will come together except one. How can I create a thread, send it a code block, get the results of that code block back, and keep the thread alive to send it another code block? Haven't found any thing like that in the docs? Does anyone have a working example of a (preferably OO) work crew style thread pool that I could use/rip off?

Thanks,
Barry

Replies are listed 'Best First'.
Re: Building a thread pool
by BrowserUk (Patriarch) on Apr 18, 2006 at 13:12 UTC

    This isn't what you want, but it is what you have asked for. And it is rather less cumbersome than Thread::Pool:

    #! perl -slw use strict; package pool; use threads; use Thread::Queue; sub new{ my $class = shift; my $nThreads = shift; my $Qin = new Thread::Queue; my $Qout = new Thread::Queue; my @threads = map{ threads->new( sub { my( $Qin, $Qout ) = @_; while( my $code = $Qin->dequeue() ) { $Qout->enqueue( eval $code ); } }, $Qin, $Qout ) } 1 .. $nThreads; return bless [ $Qin, $Qout, \@threads ], $class } sub job { my( $Qin, $Qout, $threads ) = @{ +shift }; $Qin->enqueue( shift ); return; } sub DESTROY { my( $Qin, $Qout, $threads ) = @{ +shift }; $Qin->enqueue( undef ) for @$threads; sleep 0; $_->join for @$threads; } *stop = *stop = *DESTROY; package main; my $pool = pool->new( 5); for ( 1 .. 1000 ) { $pool->job( qq[ sleep 5; printf "In thread(%d). arg:%d\n", threads->tid, $_ ] ); } sleep 10; $pool->stop; __END__ c:\test>543977 In thread(1). arg:1 In thread(2). arg:2 In thread(3). arg:3 In thread(4). arg:4 In thread(5). arg:5 In thread(1). arg:6 In thread(2). arg:7 In thread(3). arg:8 In thread(4). arg:9 In thread(5). arg:10 In thread(1). arg:11 In thread(2). arg:12 In thread(3). arg:13 In thread(4). arg:14 In thread(5). arg:15 In thread(4). arg:16 In thread(3). arg:17

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Building a thread pool
by zentara (Cardinal) on Apr 18, 2006 at 11:55 UTC
    Does anyone have a working example of a (preferably OO) work crew style thread pool that I could use/rip off?

    Here is an example using Tk Tk-with-worker-threads

    It demonstrates a "sleeping thread pool". The script allows you to set how many to make ( set up as max= 3), then it keeps an array, which you shift and push, to keep track of available (free) workers. You can pass code to it, and get results thru shared variables. I just have it open and xterm, and when you close the xterm, a count starts. When the thread is finished or cancelled, it goes back to sleep, and is pushed back into the available-array. I have the sleep time as 1 second, but you can improve responsiveness by using a miliisecond value ( select(undef,undef,undef,.01). It will still use very little cpu when sleeping at that interval.

    The sleeping thread loop is the essence of what you are after, in the sub "work". I used a goto in it, as a simple way of killing the thread , but you could use a "return" instead.

    P.S. The reason I use Tk, is that it allows for an event-loop to monitor the thread's shared vars from main. This is your big problem. The main script will NOT see changes in shared vars, until it actively reads it. So you need a loop of somekind. Maybe POE, Event, or another option like Roll your own Event-loop


    I'm not really a human, but I play one on earth. flash japh
Re: Building a thread pool
by barvin (Beadle) on Apr 18, 2006 at 03:08 UTC
    I got Thread::Pool working. Actually, it was working all along. I was just trying to drive the script with the debugger which doesn't work. However if anyone has an answer to the question about how to keep threads alive for re-use, I'd still like to know.

    Barry
      I got Thread::Pool working.

      Could you post your working version please.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

        I think the OP means that his original version works, just not under the debugger.

Re: Building a thread pool
by superfrink (Curate) on Apr 18, 2006 at 03:46 UTC
    It is is not exactly the same but Net::Server::PreFork and Net::Server::PreForkSimple will fork a set of child processes, wait for a network connection, and issue requests to the child processes.

    If these do not do what you need maybe the source code will help.

    These modules maintain a range (minimum and maximum) of child processes ready to serve requests. Each child processes are killed and respawn after it processes a set number of requests.
Re: Building a thread pool
by barvin (Beadle) on Apr 18, 2006 at 21:41 UTC
    Yes, I meant that Thread::Pool works as is if you don't try to run it under the perl debugger. In that case the test script works with minor modificaitons. The following is a simpler example that worked for me as is:
    #!/usr/bin/perl use strict; use warnings; use Thread::Pool; my $pool = Thread::Pool->new({workers => 10, do => \&do, monitor => \&monitor}); my @jobs = ('A' .. 'Z'); my $count; for (@jobs) { print $count++, "\n"; $pool->job($_); } sub do { my $letter = shift; print "begin: $letter\n"; for (1 .. 10000000) { my $x = $_ ^ 2; } print "end: $letter\n"; return "$letter is back\n\n"; } sub monitor {print shift}
    Fire is up and watch those processors hummmm! Also the lighter weight version offered by browserUK in this thread worked well for me as posted. Thanks to you all. Barry