in reply to Re: Wx subs and threads
in thread Wx subs and threads

when I incorporate it into my Wx App the process launches and the app locks up.

Most gui interfaces are not threadsafe, so you need to launch the thread BEFORE any gui code is used, and don't try to access gui widgets from the thread code block. Launching the thread before any gui code is written usally requires some shared variables to tell the thread code to start and stop. For a simple example of a sleeping thread, see PerlTk on a thread.... Of course, the while loop in the thread can become more complex, having nested loops for a working state, and a sleeping state. While in the sleeping state, it should regularily check for a shared variable telling it to run, and if you want to reuse the thread, it should check while it's running to drop back into the sleep state. Search groups.google.com for "perl tk sleeping thread" for more examples. Sorry I don't do much Wx.

Make your design to reuse threads, unless it's a 1-shot deal, because the thread ref-count cleanup is not perfect, and you will gain memory everytime you spawn a thread. Always try to reuse widgets and threads, and you will be happier in the long run.


I'm not really a human, but I play one on earth CandyGram for Mongo

Replies are listed 'Best First'.
Re^3: Wx subs and threads
by Anonymous Monk on Jun 27, 2008 at 18:31 UTC
    That makes sense but I'm a little confused as how I can start/stop my thread if it's created prior to my GUI. I've attached my (hack at this) below. Thanks for any help!
    use threads; use threads::shared; use Wx; use Thread::Queue; use strict; # Maximum working threads my $MAX_THREADS = 1; # Flag to inform all threads that application is terminating my $TERM :shared = 0; # Prevents double detach attempts my $DETACHING :shared; package MyApp; use base 'Wx::App'; sub OnInit { my $self = shift; my $frame = MyFrame->new(); $frame->Show(1); $self->SetTopWindow($frame); return 1; } package MyFrame; use base 'Wx::Frame'; use Wx qw(wxTE_MULTILINE wxVERTICAL wxID_DEFAULT); use Wx::Event qw(EVT_COMMAND EVT_CLOSE EVT_BUTTON); my $done_event : shared = Wx::NewEventType; sub new { my $class = shift; my $self = $class->SUPER::new(undef, -1, "The title"); $self->{text} = Wx::TextCtrl->new($self, -1, "", [-1,-1], [180, 10 +0], wxTE_MULTILINE); $self->{button_start} = Wx::Button->new($self, -1, "&Start Test"); $self->{button_stop} = Wx::Button->new($self, -1, "&Stop Test"); my $sizer = Wx::BoxSizer->new(wxVERTICAL); $sizer->Add($self->{text}); $sizer->Add($self->{button_start}); $sizer->Add($self->{button_stop}); $self->SetSizer($sizer); $self->{text}->AppendText("test\n"); EVT_BUTTON($self,$self->{button_start}, \&DeployTestThread ); EVT_BUTTON($self,$self->{button_stop}, \&StopTestThread ); return $self; } sub DeployTestThread { # Manage the thread pool until signalled to terminate while (! $TERM) { # Keep max threads running for (my $needed = $MAX_THREADS - threads->list(); $needed && ! $TERM; $needed--) { # Create New thread my $thread = threads->create('ExecuteTest'); } } # Detach and kill any remaining threads foreach my $thr (threads->list()) { lock($DETACHING); $thr->detach() if ! $thr->is_detached(); print "Killing " . $thr->tid() . "\n"; $thr->kill('KILL'); } print("Done\n"); } sub StopTestThread { $TERM = 1; } sub ExecuteTest { my($handler, $queue) = @_; use LWP::Simple; # For Testing purposes for (1..10) { my @sites = qw( http://www.google.com/ http://www.microsoft.com/ http://www.yahoo.com/ http://www.cpan.org/ http://www.perl.org/ ); for(0 .. $#sites) { my $page = get($sites[$_]); my ($title1) = $page =~ /<title[^>]*>\s*(.+?)<\/title[^>]* +>/gsi; my $title : shared = $title1; print "$title\n"; my $thread_event = Wx::PlThreadEvent->new(-1, $done_event, + $title); Wx::PostEvent($handler, $thread_event); } } } package main; MyApp->new->MainLoop;
      I'm not experienced with the ins and outs out Wx, but look at Reusable threads demo for the basic CLI use. Then in your Wx gui, replace the while loops in the main thread, with Wx timers, etc.

      But I can see from your example that you are starting the thread from a Wx button callback:

      EVT_BUTTON($self,$self->{button_start}, \&DeployTestThread );
      The general rule is to start the thread BEFORE any Wx gui code is invoked. So you may try something like
      # Prevents double detach attempts my $DETACHING :shared; #start your thread here before Wx DeployTestThread(); package MyApp;
      then your button callback will just set the shared variable
      EVT_BUTTON($self,$self->{button_start}, sub{ $TERM = 0 } );
      That's the idea, of course you need to setup $TERM properly. But that is the general idea of how to start the thread before the gui code, then use the gui to change a shared variable, which controls thread state.

      For what it's worth, Perl/Gtk2 has a thread safety mechanism, and you can do what you are trying to do in Gtk2. Also, when Wx builds itself, it will use Gtk2 or Xlib as it's base objects. So if your Wx was built on Gtk2, you may be able to invoke the Glib thread safety mechanism in Wx. You will need to search the docs, or ask someone more knowledgable than me about Wx.


      I'm not really a human, but I play one on earth CandyGram for Mongo