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

I've been using perl on and off for 8 years but I'm still pants at it. My current failure lies is in (not) getting a progress bar to work.

WHAT I THINK MY CODE SHOULD DO:

I simply want to have a thread print a dot to a user's screen every 10 seconds via HTTP (to keep the browser connection alive and stop it timing out and also to indicate the script is still running) until another thread finishes.

WHAT MY CODE ACTUALLY DOES:

Nothing gets printed to the screen by the progress bar. When the work thread rejoins the main thread it posts its results to the screen (if the browser hasn't timed out), or the browser times out. No sign of my

THE CODE:

# set a shared value of TRUE for running the progress bar my $keepRunningProgressBar : shared = 1; # Set the progress bar running indefinitely my $progressBar = threads->create(sub { while ($keepRunningProgressBar + == 1) {print "<img src=\"http://www.thepaty.plus.com/dot.gif\">"; sl +eep(10);} }); $progressBar->detach; # Now do stuff in the work thread my $threadForSpidering = threads->new(\&workthread, $category, $conten +ts, $userRunningBot,$userIPAddress); #The work thread finished $threadForSpidering->join; #And tells the progress bar to stop running $keepRunningProgressBar = 0;

What am I doing wrong here??

Cheers Dan

Replies are listed 'Best First'.
Re: Progress bar with (i)threads
by MaxKlokan (Monk) on Dec 08, 2006 at 12:56 UTC
    Have you tried disabling buffering with autoflush?
    Just add:
    $|=1;
    at the top of your script.
      That did the trick. I hadn't come across autoflush before (I guess because I'd never done this sort of thing before with perl). Progress bar is now working.

      Thank you Perl Monks, you clever people you :-)

      Dan
Re: Progress bar with (i)threads
by gaal (Parson) on Dec 08, 2006 at 11:26 UTC
    Are you suffering from buffering? Flush your filehandle after the print and see if it helps. One way to do that in your case would be simply to add a newline to the string.
      Thank you for the reply. I'm not saying its definitely not a buffering problem but I don't think it is since it doesn't get printed EVER, even when the other thread is joined and prints its results. I tried adding a newline character to the end of the string, no difference.

      Additionally, when the main thread exits it warns "A thread exited while 2 threads were running" as if the progress bar thread was still merrily churning away doing something (frustratingly, not printing a progress bar) when the main thread exited.

      Anything else I should be checking?
      Thanks
      Dan
Re: Progress bar with (i)threads
by clinton (Priest) on Dec 08, 2006 at 13:38 UTC
    The problem with the script exiting while there are still threads running is one of timing. The main thread exits before the progressBar has had a chance to finish sleeping for 10 and check $keepRunningProgressBar.

    The problem with the lack of output is almost definitely a buffering issue. Try making the request using wget or with telnetand see exactly what is being returned.

    This code demonstrates that the logic works, and it outputs a dot every 10 seconds, but only forces the user to wait a maximum of one second more than the process would have taken anyway:

    #!/usr/bin/perl use strict; use warnings; use threads; use threads::shared; # set a shared value of TRUE for running the progress bar my $keepRunningProgressBar : shared = 1; # Set the progress bar running indefinitely my $progressBar = threads->create( sub { my $i = 0; while ($keepRunningProgressBar == 1) { if ($i++ % 10 == 0) { print ".\n"; } sleep(1); } } ); # Now do stuff in the work thread my $threadForSpidering = threads->new( sub { my $i = 0; while ($i++<5) { print "Working\n"; sleep 1; }}); #The work thread finished $threadForSpidering->join; #And tells the progress bar to stop running $keepRunningProgressBar = 0; $progressBar->join; print "\nGOT HERE\n";
    UPDATE I have never used threads before, but I see that the version of threads.pm that came with my perl is old 1.07, and with the current version you could do something like this:
      Thank you. You are of course right about the timing. That's very simple, I should have seen that. No warnings now when the main thread exits. Thanks!
Re: Progress bar with (i)threads
by zentara (Cardinal) on Dec 08, 2006 at 13:48 UTC
    Here is a version that works for me. Don't forget to print your header first, to get things going.
    #!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; print "Content-type: text/html\n\n"; print "Starting work<br>\n"; $|++; # set a shared value of TRUE for running the progress bar my $keepRunningProgressBar : shared = 1; # Set the progress bar running indefinitely my $progressBar = threads->create( \&dot_thread )->detach; # Now do stuff in the work thread my $threadForSpidering = threads->new( \&workthread ); #The work thread finished #$threadForSpidering->join; #you have 3 threads, not just one #main, dot_thread, and workthread #And tells the progress bar to stop running $keepRunningProgressBar = 0; #<>; #without waiting here you get the thread exited error foreach my $thread ( threads->list ) { $thread->join; } print "done<br>.\n"; exit; ########################################################## sub dot_thread { $|++; while ( $keepRunningProgressBar == 1 ) { print ".\n"; sleep( 1 ); } } sub workthread { for ( 1 .. 10 ) { print "\tworking\n"; sleep 1; } }

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Progress bar with (i)threads (Buffering)
by Anonymous Monk on Dec 08, 2006 at 13:49 UTC
    This is a perl script running as a cgi script, right?

    Other people have already mentioned setting $| = 1 to stop perl buffering the output.

    You also need to stop your web server buffering it. Ensure that your script’s filename starts nph-. (NPH stands for non-parsed headers.) You will also need to ensure that the http headers your script writes are terminated by a CRLF, as the web server will no longer do that for you.

      This is a Perl-language CGI script, yes. Its actually for one of several bots I'm writing to help out with wikipedia, if anyone's interested :-)

      The buffering was causing a problem - it wasn't something I'd come across before so I didn't really know where to look for the cause of the problem - since it was the first time I'd used threads I assumed that I must be doing something wrong with the threads code it seems my threads code was actually ok.

      Thanks for the response!