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

Hi monks.

Taking the above codes as an example, I found that not every thread is
re-collected right after the join is completed when using join to
perform the re-collection job. The memory will be re-cycled after all
the thread in @thread has been "join". When applying to a practial
job, it will be not satisfied. However, if using
"threads->new("StartTest")->detach" directly, there is no such
problem. Why?

-----------8<------------

#!/usr/bin/Perl use 5.008; use strict; use threads; use threads::shared; $| = 1; my @threads; foreach (1 .. 20) { push @threads, threads->new('StartTest'); # threads->new('StartTest')->detach; threads->yield; } while (my $thread = shift @threads) { $thread->join; } sleep 10; print "\n", "All threads done\n"; sub StartTest { my $self = threads->self; my @m = (1 .. 99999); print "Thread ", $self->tid, " started\n"; sleep ($self->tid + 5); print "Thread ", $self->tid, " ending\n"; } 1; __END__

Replies are listed 'Best First'.
Re: The problem on re-collection thread using join
by BrowserUk (Patriarch) on Dec 01, 2004 at 18:21 UTC
    However, if using "threads->new("StartTest")->detach" directly, there is no such problem. Why?

    join() waits until the thread code completes (ie. returns or falls off the end of the sub) before returning. In your example,

    1. the first thread you create waits for 6 seconds before dying.
    2. the second thread waits 7 seconds.
    3. the third thread waits 8. and so on.

    So then your main thread waits for the first thread to die, then the second, then the third etc. But none of the threads can complete until the previous threads have returned (joined), so even when their timeout occurs, they still have to hang around for the main thread.

    And remember, threads are non-deterministic. That means that your main thread code may never get a timslice in which to join the first thread, until the last thread (20) has waited it's 21+5 seconds.

    Unfortunately, the threads API does not support a joinFirst() or joinNextReady() call (think forks and wait) that simply collects the first available dead thread.

    You can only join a specified (via thread object) thread. And join will never return ,until that thread ends, regardless of whether there are other dead threads ready to be joined.

    This makes using join almost useless, because if a thread hangs, and you attempt to join to it, the joining thread also hangs, which makes a complete mockery of multi-tasking!

    I've completely given up trying to make use of the join call, and now detach all my threads, and arrange to return values via other, safely asynchronous means instead.


    <rant type="personal" target="not the questioner">

    This is (another) fundamental flaw in the underlying API upon which Perl's iThreads are modelled.

    I seriously doubt if there any sufficiently skilled Perl divers around with enough interest in iThreads to try and fix this, but please, let those designing Parrot's threads be listening and take notice.

    </rant>


    Examine what is said, not who speaks.
    "But you should never overestimate the ingenuity of the sceptics to come up with a counter-argument." -Myles Allen
    "Think for yourself!" - Abigail        "Time is a poor substitute for thought"--theorbtwo         "Efficiency is intelligent laziness." -David Dunham
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
Re: The problem on re-collection thread using join
by zentara (Cardinal) on Dec 02, 2004 at 13:57 UTC
    I'm in no position to argue against BrowserUk's expertise concerning threads, but sometimes us "amateur" programmers get stuck on using simple methods when faced with complex problems. In your case, BrowserUk showed you the problem of the threads needing to "finish their code block" before they can join.

    What I have taken to doing is putting a FINISH: label at the end of the thread-code-block, and when I want to join a thread, I send it a die signal thru a shared variable. In the thread-code, test for the shared-var in your loop, and if "condition is met", put a "goto FINISH" in the statement. Then whenever you want to join the thread, you do 2 things, send the die, then join. I like using hashes to keep track of it all. Also, if you "detach" you can't join.

    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; $| = 1; my %threads; foreach (1..10){ share ($threads{$_}{'die'}); $threads{$_}{'die'} = 0; } foreach (1..10) { $threads{$_}{'thread'} = threads->new('StartTest'); } my @threads = (1..10); foreach(@threads){ $threads{$_}{'die'} = 1; $threads{$_}{'thread'}->join; } print "\n", "All threads done\n"; sub StartTest { my $self = threads->self; print "Thread ", $self->tid, " started\n"; while(1){ if( $threads{$_}{'die'} == 1){goto FINISH} else{ select(undef,undef,undef, 10) }; } FINISH: print "Thread ", $self->tid, " ending\n"; }

    I'm not really a human, but I play one on earth. flash japh