in reply to "Thread already joined at..." and "A thread exited while x threads were running" errors

There are a lot of "anomolies" in your script.

The primary cause of the errors you are complaining of is becasue you are using a global variable (despite the my applied to it's declaration) to store your thread handles.

This my @threadList = (); declares the array, but this array is then reused over and over within a subroutine, within nested loops:

sub runTests { ... foreach my $userType (... for my $i ( ... push @threadList, threads->new( ... } } ... foreach my $thread (... $thread->join; } }

So, each time through, you are pushing thread handles into that array, and then later joining them--but you never remove them!

Which means that the second time through, you still have all the (joined) thread handles from the first run, and you attempt to re-join these, and get errors.


A few others:

sort { $allScripts{$a} <=> $allScripts{$b} } keys %allScripts

The keys to this hash are strings, but you are using the numeric comparator (<=>), which means you are also getting a load of Argument "..." isn't numeric in numeric comparison (<=>) at... which you are apparently ignoring.

Your comment:

# key-value pairs in a Perl hash are not always # stored in the order in which they were defined!

is very misleading--mostly because it is so nearly true :) The order in which keys are returned by the keys operator is "Not well defined". In versions prior to 5.8.something, the order was determined by a series of internal storage details that means it was "essentially random". In later versions, the return order is deliberately randomised for security reasons, so it is "actually random".

If it is important to your application that you can iterate your data structure in a specific order, you should either use an array -or- one of the many "sorted hash" modules from CPAN.

Finally, I'm not sure what your script is trying to achieve, so this comment may be way off base, but in general, "threads" and "timing" do not go together.

Threads are non-determanistic, which is to say, the time between your program asking a thread to start, and that thread actually getting a timeslice and running is determined by the OS according to a variety of factors that are entirely beyond prediction--system load, number of cpu's, whether other threads and processes in the system are cpu-bound or IO-bound; and a whole host of other factors.

Timing things that are running on concurrent threads is about as meaningful as a chocolate teapot.


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail
"Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
  • Comment on Re: "Thread already joined at..." and "A thread exited while x threads were running" errors
  • Select or Download Code

Replies are listed 'Best First'.
Re^2: "Thread already joined at..." and "A thread exited while x threads were running" errors
by nickos (Initiate) on Sep 06, 2004 at 15:44 UTC
    "This my @threadList = (); declares the array, but this array is then reused over and over within a subroutine, within nested loops:"

    Yeah, I realised that after reading the first reply - moving the declaration into the scope of the runTests() subroutine fixes the initial problem but I'm still seeing the script hang. To check it wasnt the non-Perl interpreter which I'm trying to load-test that's crashing, I changed the script slightly to run DOS batch files instead, but I still get problems.

    "The keys to this hash are strings, but you are using the numeric comparator (<=>), which means you are also getting a load of Argument "..." isn't numeric in numeric comparison (<=>) at... which you are apparently ignoring."

    I don't seem to be getting those warnings. Any ideas?

    "Finally, I'm not sure what your script is trying to achieve, so this comment may be way off base, but in general, "threads" and "timing" do not go together."

    My company sells a proprietary system that sits on top of Apache, and we're trying to profile part of our software so that we can make accurate recommendations to customers when they ask what sort of hardware they should use with our software. My plan is to run the bulk of the code many times to get an average speed (see timethis($numCompleteTestRuns, 'runTests');).

      Without sight of the modified script, plus the bat files, I could only guess. A word of caution though, if your not already using 5.8.4 or 5.8.5, upgrade before continuing. Many early bugs in ithreads where fixed in these builds. There is no point in re-discovering them:)

      I don't seem to be getting those warnings. Any ideas?

      No!?

      use Benchmark qw[ timethis ]; timethis( 10, sub { my $x = 'A' <=> 'B' } ); Argument "B" isn't numeric in numeric comparison (<=>) at (eval 12) li +ne 1. Argument "A" isn't numeric in numeric comparison (<=>) at (eval 12) li +ne 1. timethis 10: 0 wallclock secs ( 0.00 usr + 0.00 sys = 0.00 CPU) (warning: too few iterations for a reliable count)
      ... and we're trying to profile part of our software so that we can make accurate...

      I can only re-emphasis. "Timing" + "threads" !== "accurate". In fact, it will not even be consistant. This is not an error (of threads or Perl's ithreads), it's just a fact of life with preemptive multi-processing.

      With the additional information, and another look at your script, it looks like your trying to simulate a server environment and draw conclusions based upon that simulation. This will not work. Your simulation is simply not accurate, nor representative enough to be of any merit.

      If you need to profile, then you should be using a real profiler on the real code in the real environment, or as close to it as is possible to achieve.

      That's a pretty flat judgement based on very little information, so don't be offended. You may know what your doing is good enough for your purpose--it just doesn't look like it from where I am sitting :)


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

        Hi again,

        I'm using version 5.8.4, so hopefully I'm not experiencing a bug in Perl.

        I'm trying to find a way of doing a hash that retains its order. I've looked on CPAN as you suggested, but both Tie::Hash::Indexed and Tie::InsertOrderHash which look suitable require compilation, and I was hoping to find something that could be copied from one platform to the other without any additional work (we have an old Python script which is very easy to use, and I want to at least equal that in terms of ease of use).

        As for your final point about whether this is a worth while exercise, I have been told that it is as we need to be able to be able to compare our system when running on machines with different processors and operating systems, so issues like scheduling etc are one of the variables we need to look at. Hopefully by running the test many times we can get an average figure which reflects platform performance reasonably well.

        I've given up on using batch files - they were worse than our script interpreter, as they were crashing the command prompt window (isn't Windows great). I'm now running some tiny exes I've just compiled in C that just printf() a character but the script still hangs. Here's the script so far:

        use Acme::Comment type => 'C++', own_line => 1, one_line => 1; use Cwd; use Run; use Config; use Data::Dumper; use threads; //use Async; use Benchmark qw(:all); use strict; use warnings; # Change this structure accordingly... my %allScripts = ( submitters => { numRuns => 4, steps => ["print1.exe", "print2.exe"] }, reviewers => { numRuns => 1, steps => ["printA.exe", "printB.exe"] } ); my $numCompleteTestRuns = 4; my $mscriptInterpreter = "ScriptRunner.exe"; my $currentDir = getcwd(); eval{timethis($numCompleteTestRuns, 'runTests')}; warn $@ if $@; sub runTests { print "Waiting to spawn processes.\n"; foreach my $userType (sort {$allScripts{$a} <=> $allScripts{$b}} k +eys %allScripts) { // print Dumper($allScripts{$userType}{steps}); print "\nThe $userType will run $allScripts{$userType}{numRuns +} times. The steps are:\n"; foreach my $i (0 .. $#{ $allScripts{$userType}{steps}}) { print "$i = $allScripts{$userType}{steps}[$i]\n"; } } print("\n\n"); my @threadList = (); # key-value pairs in a Perl hash are not always stored in the orde +r in which they were defined! // foreach my $userType (keys %allScripts) foreach my $userType (sort {$allScripts{$a} <=> $allScripts{$b}} k +eys %allScripts) { for my $i (0 .. $allScripts{$userType}{numRuns}) { print "\nAbout to create $userType thread $i:\n"; my $thread = threads->new(\&stepThread, \$allScripts{$user +Type}); push (@threadList, $thread); } } my $numThreads = $#threadList + 1; print("There are $numThreads threads\n\n"); # wait for each running thread to end foreach my $thread (@threadList) { $thread->join; print("A thread joined\n\n"); } print "Processes ended - Goodbye.\n"; } # thread runs a type of users steps once only sub stepThread { print("starting thread\n\n"); my $currentUsertype = shift; // print Dumper(${$currentUsertype}->{steps}); foreach my $i (0 .. $#{${$currentUsertype}->{steps}}) { my $fileName = ${$currentUsertype}->{steps}[$i]; // my $command = $mscriptInterpreter . " \"" . $currentDir . "/ +" . $fileName . "\"\n"; my $command = $fileName . "\"\n"; print("About to run: $fileName\n"); my $time0 = new Benchmark; my $output = `$command`; my $time1 = new Benchmark; my $timeTaken = timestr(timediff($time1, $time0)); my $printOutput = "******** BEGIN $fileName output ********\n$ +output\n******** END $fileName output (time taken: $timeTaken) ****** +**\n"; print($printOutput); } print("Ending thread\n\n"); }