in reply to Threads question

One thing that hasn't been mentioned clearly, is how to cleanly end a thread prematurely. All of the discussion above assumes that the thread will finish it's processing then be joined..... but what if you want to join the thread BEFORE it has completed it's code block?

The basic rule to remember, is that in order for a thread to be joined, it must reach the end of it's code block, OR do a return. You usually use a shared variable, such as $die, to signal the thread to return. Then you can have the main thread set $die=1, then in the thread code have

return if $die;
at crucial code points.

This brings up the next big issue you will face, and that is thread code being repeatedly called, can cause a memory gain, because of the imperfect garbage collection of Perl. So..... if your program needs to constantly spawn and join threads, you may need to setup a scheme to reuse threads, and put them in a sleep loop when you don't need them, and feed them a wakeup call and fresh data when you need them.

There are many examples of this in the nodes here at perlmonks, just search for "threads" and look at all the code examples.( Or search groups.google.com for "perl threads")

A final point, changes in shared variable's values are not automatically seen between threads. Each thread needs to run a timer or loop to constantly check them.


I'm not really a human, but I play one on earth. Cogito ergo sum a bum

Replies are listed 'Best First'.
Re^2: Threads question
by BrowserUk (Patriarch) on Jun 29, 2007 at 14:19 UTC
    This brings up the next big issue you will face, and that is thread code being repeatedly called, can cause a memory gain, ...

    When was the last time you observed this behaviour?

    Running the following snippet for 10,000 thread creation/death cycles, the memory bobs up and down (under win32 thread local memory gets returned to the system). The memory usage fluctuates between 7.4MB and 11.3 MB and ends at 7.1 MB just before it ends.

    So there is no obvious pattern of growth (and no big issue I can see), despite the closures, under 5.8.6 or 5.8.8. I haven't tried it under 5.9.x yet.

    If you could post code,or modify that below, to demonstrated the problem ?

    #! perl -slw use strict; use threads; use threads::shared; our $N ||= 1000; our $aClonedGlobal = 12345; my $aClonedLexical = 12345; our $aSharedGlobal :shared = 12345; my $aSharedLexical :shared = 12345; my $running :shared = 0; for ( 1 .. $N ) { async{ { lock $running; ++$running } my $tid = threads->self->tid; my( $some, $thread, $local, $vars ) = (12345) x 4; require Carp; require IO::Socket; require Time::HiRes; print "$tid: $aClonedGlobal : $aClonedLexical : $aSharedGlobal + : $aSharedLexical"; { lock $running; --$running }; }->detach; } sleep 1 while $running;
    A final point, changes in shared variable's values are not automatically seen between threads. Each thread needs to run a timer or loop to constantly check them.

    I don't understand what you mean by this? Shared vars are tied. Every time you reference one, the current value is retrieved from the master copy. How can "changes ... not [be] automatically seen between threads.", unless you reference them?

    May be I am missing something? Again, could you post an example to demonstrate what you mean by this please?


    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.
      Well I ran your code on linux( upping the count to 5000), and it starts out at about 4.4% mem (I have a gig of ram). After 2 minutes, it was up to 12.4 % ram. That is roughly a doubling of mem every minute...... that's what I'm talking about.

      Also your code is quite simple, meaning it uses modules that "probably" are quite thread safe. I would next start trying to run something like WWW::Mechanize, or LWP::UserAgent in the threads, and see what they add to the garbage leftovers. Maybe Win32 does a better job with garbage collection than linux?

      I don't understand what you mean by this? Shared vars are tied. Every time you reference one, the current value is retrieved from the master copy.

      In a simple example you are right. I'm talking about (and I should have mentioned it) using GUI's with threads. Gui's will allow you to tie, but that won't work across threads. Here is a simple example, the count is not automatically reflected to the thread.

      #!/usr/bin/perl use strict; use warnings; use threads; use threads::shared; use Tk; my $count:shared=0; my $thread = threads->new( \&launch_thread )->detach; my $mw = MainWindow->new(); $mw->geometry("600x400+100+100"); my $l1 = $mw->Label(-textvariable => \$count)->pack(); MainLoop; sub launch_thread { while (1){ $count++; print "$count\n"; sleep 1; } }

      I'm not very good with a simple Tie, but the following dosn't trigger the fetch or store, in a non-gui script.

      #!/usr/bin/perl use strict; use warnings; use threads; use threads::shared; use Tie::Watch; my $count:shared=0; my $thread = threads->new( \&launch_thread )->detach; my $watch = Tie::Watch->new( -variable => \$count, # -debug => 1, -fetch => \&fetch, -store => \&store, -destroy => sub {print "Final value=$count.\n"}, ); while(1){} sub launch_thread { while (1){ $count++; print "$count\n"; sleep 1; } } sub store { print "changed\n"; } sub fetch{ print "fetched @_\n"; }

      Maybe you can make the simple Tie report a store?


      I'm not really a human, but I play one on earth. Cogito ergo sum a bum
        Well I ran your code on linux( upping the count to 5000), and it starts out at about 4.4% mem (I have a gig of ram). After 2 minutes, it was up to 12.4 % ram.

        Not unexpected. As there is no control on the number of threads running concurrently, you have to wait for the system to reach a 'steady state' as far as the number of threads running is concerned--ie. wait to the point that new threads are being started at teh same rate the old ones are dieing--before you can no longer attribute the growth to just more concurrent threads. Otherwise you have to wait until it ends and see what the final usage is.

        I should write a better test. One that maintains a steady number of concurrent threads by starting a new only when an old one dies after a set number. That would allow a better picture.

        Also your code is quite simple, meaning it uses modules that "probably" are quite thread safe.

        Um. Loading non-threadsafe modules in threads and not expecting problems is a bit naive :)--but I get your point. When I write a better test, I'll try and load some more complex, heavy modules.

        How about you try having Tk watch a tied variable--without threads. I guessing that it won't be able to successfully tie a tie (Windsor knot anyone? :). That is, I think the problem may not be threads::shared per se, but simply that tieing a tied variable doesn't work. Essentially the same reason that you can't share a tied variable.

        I'm not sure how you would verify that. It kinda hard to modify the first level of tie whilst the second level watches without using threads? Maybe you could set up a timer (after or repeat) to modify a simple tied scalar, and then pass that tied scalar to as the watched variable to

        tie my $count, 'Tie::Scalar'; ... $mw->repeat( sub { $count++ }, 1000 ); ... my $l1 = $mw->Label(-textvariable => \$count)->pack();

        I'd try it myself, but my Tk install is screwed ever since I tried install tcl/tk. I never got around to trying to sort that mess out.


        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.