in reply to Tk and Threads

Here is a small sample (taken almost verbatim from the threads POD), demonstrating cancelling threads from a Tk gui:

#!perl -slw use strict; use Time::HiRes qw [ sleep ]; use threads; use Thread::Queue; sub work{ my( $id, $delay, $Q ) = @_; my $n = 0; local $SIG{QUIT} = sub{ threads->exit; }; while( sleep( $delay ) && ++$n <= 100 ) { $Q->enqueue( "$id:$n" ); } } my $Q = new Thread::Queue; my @threads = map threads->new( \&work, $_, 0.1 * $_, $Q ), 1 .. 2; require Tk::ProgressBar; my $mw = MainWindow->new; my $pb1 = $mw->ProgressBar()->pack(); my $bt1 = $mw->Button( -text => 'Cancel thread 1', -command => sub { $threads[0]->kill('QUIT') } )->pack; my $pb2 = $mw->ProgressBar()->pack(); my $bt2 = $mw->Button( -text => 'Cancel thread 2', -command => sub { $threads[1]->kill('QUIT') } )->pack; my $repeat; $repeat = $mw->repeat( 100 => sub { while( $Q->pending ) { my( $id, $progress ) = split ':', $Q->dequeue; return unless $progress; ( $id == 1 ? $pb1 : $pb2 )->value( $progress ); if( $id == 2 && $progress == 100 ) { $repeat->cancel; $mw->exit; } } } ); $mw->MainLoop; END{ $_->join for @threads; }

Wrapping that over in a bunch of unnecessary verbose complexity is left as an exercise for the reader :)


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.

Replies are listed 'Best First'.
Re^2: Tk and Threads
by Dirk80 (Pilgrim) on Dec 22, 2010 at 09:51 UTC

    Thank you for your reply.

    This is a good example for thread signalling. But one problem remains. You start your threads before Tk Code. And you are able to cancel your threads. But how could you restart your threads? If I am right this should not be done from Tk.

    I tried the signalling with my example with the states in the thread. My goal is it to change the state from STATE_WORK to STATE_IDLE when the signal comes. But always when the signal comes the thread is completely killed.

      With a minor change to the code I posted, the button can be used to pause & resume the threads:

      #!perl -slw use strict; use Time::HiRes qw [ sleep ]; use threads; use Thread::Queue; use constant { IDLE => 0, WORK => 1 }; sub work{ my( $id, $delay, $Q ) = @_; my $n = 0; my $state = WORK; local $SIG{QUIT} = sub{ $state ^= 1 }; while( sleep( $delay ) && ++$n <= 100 ) { sleep 1 while $state == IDLE; $Q->enqueue( "$id:$n" ); } } my $Q = new Thread::Queue; my @threads = map threads->new( \&work, $_, 0.1 * $_, $Q ), 1 .. 2; require Tk::ProgressBar; my $mw = MainWindow->new; my $pb1 = $mw->ProgressBar()->pack(); my $bt1 = $mw->Button( -text => 'Pause/resume thread 1', -command => sub { $threads[0]->kill('QUIT') } )->pack; my $pb2 = $mw->ProgressBar()->pack(); my $bt2 = $mw->Button( -text => 'Pause/resume thread 2', -command => sub { $threads[1]->kill('QUIT') } )->pack; my $repeat; $repeat = $mw->repeat( 100 => sub { while( $Q->pending ) { my( $id, $progress ) = split ':', $Q->dequeue; return unless $progress; ( $id == 1 ? $pb1 : $pb2 )->value( $progress ); if( $id == 2 && $progress == 100 ) { $repeat->cancel; $mw->exit; } } } ); $mw->MainLoop; END{ $_->join for @threads; }

      But that is actually easier to achieve using a simple shared variable.


      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.

        Pausing and resuming is great. But what I need is a restart.