Dirk80 has asked for the wisdom of the Perl Monks concerning the following question:
Hello,
I have written a small example how I use threads with TK at the moment.
It is working, but it is polluting my design and code. So I'm sure that there has to be a better solution than mine.
First I want to present you my solution:
thread_example.pl:
#!/usr/bin/perl ########################################################### # USES ########################################################### use strict; use warnings; use Tk; use My::Gui; use My::ThreadManager; ########################################################### # MAIN FUNCTION ########################################################### &createMyThread(); &createMyGui(); &MainLoop();
My/Globals.pm:
package My::Globals; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use constant { FALSE => 0, TRUE => 1, CANCEL => 2 }; ########################################################### # EXPORT ########################################################### our @EXPORT = qw(TRUE FALSE CANCEL); ########################################################### # VARIABLES ########################################################### # NONE ########################################################### # PUBLIC FUNCTIONS ########################################################### # NONE ########################################################### # PRIVATE FUNCTIONS ########################################################### # NONE 1;
My/Gui.pm:
package My::Gui; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use Tk; use My::ThreadManager; ########################################################### # EXPORT ########################################################### our @EXPORT = qw(createMyGui); ########################################################### # VARIABLES ########################################################### my %gui; my %thread_data = ( 'number' => 7 ); ########################################################### # PUBLIC FUNCTIONS ########################################################### sub createMyGui { $gui{'mw'} = new MainWindow; $gui{'mw'}->title("Thread Example"); $gui{'mw'}->protocol('WM_DELETE_WINDOW' => sub { evWorkCancel() unless isWorkFinished(); killMyThread(); }); fillMainWindow($gui{'mw'}); } ########################################################### # PRIVATE FUNCTIONS ########################################################### sub fillMainWindow { my $mw = $_[0]; $mw->{'start_b'} = $mw->Button('-relief' => 'raised', '-text' => 'START', '-command' => sub { evWorkStart(\%thread_data); }); $mw->{'cancel_b'} = $mw->Button('-relief' => 'raised', '-text' => 'CANCEL', '-command' => sub { evWorkCancel(); }); $mw->{'start_b'}->pack(); $mw->{'cancel_b'}->pack(); } 1;
My/ThreadManager.pm:
package My::ThreadManager; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use Data::Dumper; use threads; use threads::shared; use Thread::Queue; use My::Globals; use My::ThreadWorker; use feature qw(say); use constant { EV_NONE => 0, EV_KILL => 1, EV_WORK_START => 2, EV_WORK_CANCEL => 3, EV_WORK_FINISH => 4 }; use constant { STATE_IDLE => 0, STATE_WORK => 1 }; ########################################################### # EXPORT ########################################################### our @EXPORT = qw(createMyThread killMyThread evWorkStart evWorkCancel isWorkFinished shallWorkBeCancelled ); ########################################################### # VARIABLES ########################################################### my $thread; my $thread_event:shared = EV_NONE; my $thread_state:shared = STATE_IDLE; my $q = Thread::Queue->new(); ########################################################### # PUBLIC FUNCTIONS ########################################################### sub createMyThread { $thread = threads->create( \&execMyThread ); } sub execMyThread { STATE_IDLE: $thread_state = STATE_IDLE; say "STATE_IDLE"; while(1) { if( $thread_event == EV_WORK_START ) { say "EV_WORK_START"; $thread_event = EV_NONE; goto STATE_WORK; } elsif( $thread_event == EV_KILL ) { say "EV_KILL"; $thread_event = EV_NONE; return; } else { # wait select(undef,undef,undef,0.1); } } STATE_WORK: $thread_state = STATE_WORK; say "STATE_WORK"; while(1) { if( ($thread_event == EV_WORK_FINISH) || ($thread_event == EV_WORK_CANCEL) ) { say "EV_WORK_FINISH" if( $thread_event == EV_WORK_FINISH ) +; say "EV_WORK_CANCEL" if( $thread_event == EV_WORK_CANCEL ) +; $thread_event = EV_NONE; goto STATE_IDLE; } elsif( $thread_event == EV_KILL ) { say "EV_KILL"; $thread_event = EV_NONE; return; } else { my %thread_data; select(undef,undef,undef,0.1); while( my $thread_data_str1 = $q->dequeue ) { %thread_data = %{ eval $thread_data_str1 }; }; if( &work(\%thread_data) == CANCEL ) { $thread_event = EV_WORK_CANCEL; } else { $thread_event = EV_WORK_FINISH; } } } } sub killMyThread { $thread_event = EV_KILL; $thread->join; exit 0; } sub evWorkStart { my $ref_thread_data = $_[0]; my $thread_data_str1 = ""; $Data::Dumper::Varname = "thread_data_str"; $thread_data_str1 = Dumper($ref_thread_data); $q->enqueue( $thread_data_str1 ); $q->enqueue( undef ); $thread_event = EV_WORK_START; while( $thread_state != STATE_WORK ) { select(undef, undef, undef, 0.1); } } sub evWorkCancel { $thread_event = EV_WORK_CANCEL; while( $thread_state != STATE_IDLE ) { select(undef, undef, undef, 0.1); } } sub isWorkFinished { if( $thread_state == STATE_IDLE ) { return TRUE; } else { return FALSE; } } sub shallWorkBeCancelled { if( $thread_event == EV_WORK_CANCEL ) { return TRUE; } else { return FALSE; } } ########################################################### # PRIVATE FUNCTIONS ########################################################### # NONE 1;
My/ThreadWorker:
package My::ThreadWorker; ########################################################### # USES ########################################################### use strict; use warnings; use base 'Exporter'; use My::Globals; use My::ThreadManager; use Data::Dumper; use feature qw(say); ########################################################### # EXPORT ########################################################### our @EXPORT = qw( work ); ########################################################### # VARIABLES ########################################################### # NONE ########################################################### # PUBLIC FUNCTIONS ########################################################### sub work { my ($ref_thread_data) = @_; while( $ref_thread_data->{'number'} <= 100 ) { print $ref_thread_data->{'number'} . " "; select(undef, undef, undef, 0.1); $ref_thread_data->{'number'} += 1; # NOTE: # Bad design because My::ThreadWorker needs # My::ThreadManager and My::ThreadManager # needs MyThreadWorker if( My::ThreadManager::shallWorkBeCancelled() == TRUE ) { print "\n"; return CANCEL; } } print "\n"; }
In reality the work function of package "My::ThreadWorker" is a complex function which is calling a lot of other modules. I want to be able to cancel this work function to every time and NOT only when it is calling My::ThreadManager::shallWorkBeCancelled.
I do not like to make the call to this function. Because I have to enter this call to a lot of code. In my opinion the code is polluted.
Is there a solution to return from the work function when the user is pressing the cancel-button without polluting the code with My::ThreadManager::shallWorkBeCancelled?
And the other problem is the design. "ThreadWorker" needs "ThreadManager" and "ThreadManager" needs "ThreadWorker".
Thank you alot for your help
Greetings,
Dirk
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Tk and Threads
by zentara (Cardinal) on Dec 21, 2010 at 17:39 UTC | |
by Dirk80 (Pilgrim) on Dec 21, 2010 at 21:31 UTC | |
by zentara (Cardinal) on Dec 22, 2010 at 12:35 UTC | |
by BrowserUk (Patriarch) on Dec 22, 2010 at 13:23 UTC | |
by zentara (Cardinal) on Dec 22, 2010 at 20:31 UTC | |
|
Re: Tk and Threads
by BrowserUk (Patriarch) on Dec 22, 2010 at 00:09 UTC | |
by Dirk80 (Pilgrim) on Dec 22, 2010 at 09:51 UTC | |
by BrowserUk (Patriarch) on Dec 22, 2010 at 10:31 UTC | |
by Dirk80 (Pilgrim) on Dec 22, 2010 at 10:56 UTC | |
by BrowserUk (Patriarch) on Dec 22, 2010 at 11:04 UTC | |
| |
|
Re: Tk and Threads
by roboticus (Chancellor) on Dec 21, 2010 at 15:28 UTC |