Fine, thanks for such a prompt reply! I'll have a look at your code now. I'm already proceeding with a similar scheme, where all worker threads are created before any Gtk stuff and instructed via queues to eval strings (the code in not known in advance). I was just hoping that perhaps the development leaped ahead. :) Btw, earlier I found one of your scripts with a similar approach (attached as thr.pl), but that also gives me a nasty error on exit: dave jeeves:~$ perl thr.pl GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'. GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'. GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'. GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'. GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'. GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `<invalid>'. GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT (object)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `(null)'. GLib-GObject-CRITICAL **: g_signal_emit_valist: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `(null)'. GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. GLib-GObject-WARNING **: instance of invalid non-instantiatable type `(null)'. GLib-GObject-CRITICAL **: g_signal_handlers_destroy: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed. Segmentation fault A bit unsettling as you create the thread before Gtk2->main, which seems to be considered quite safe. :) The only threaded script I found working 100% so far is thread_usage.pl from some Gtk2-perl tutorial. Interesting is that it seems to create threads at the same point like you (after Gtk2 widget stuff, before Gtk2->main) and it works. It even touches the GUI from other threads. All this makes me a bit nervous about Gtk-perl. Looks like a lot of depends on "unrelated" conditions. My app is gonna have a lot of IPC and threads and I'm seriously thinking about moving to good old Gtk/C, which would mean MUCH more code, time, testing... :( -- David Kubíček System Specialist T-Systems Czech Republic a.s. Na Karmeli 1457 Mladá Boleslav 29301, Czech Republic Mobile: (+420) 739 242 055 Phone: (+420) 236 099 459 PGP: http://awk.cz/key.pgp http://awk.cz/key.pem Email: kubicek gedas cz david kubicek t-systems com kubicek awk cz Connection to localhost closed by remote host. zentara wrote: On Mon, 24 Mar 2008 14:47:05 +0100 David Kubíček <dave awk cz> wrote:Hello, I'm having problems with threads in my GTK application. You can see exactly what I'm doing from the attached files. Both were originally a testing script I found here when browsing archives, so please ignore formatting, etc - they're not mine. :) IHi, I get the same results as you running your scripts. I have 2 examples to show you. The first, is almost like yours, and the thing you will notice, is how you need to turn off thread safety (with explanation by muppet), AND you need to use timers and Idle->add to manipulate the $pbar from the thread. Threads can be very tricky. If you want foolproof thread operation, you need to make reusable(I call them sleeping threads). I have one in the second example below. In the third example, I control 3 sleeping threads. These all run well, and you might want to adapt the sleeping thread model. It is possible that you may be able to spawn threads dynamically, and destroy(detach) them when done, but almost every attempt to do that (which I've seen) fails with errors after a few invocations. So, threads can be used safely, but at the current state of development, you need to reuse threads and control them thru shared variables. Don't try to make "disposible detached threads". They may seem to work, but you will often get a memory gain for each thread, and they fail eventually with repeated calls. GoodLuck, remember to use Timeouts instead of sleep, and use Glib::Idle->add() from your thread code block to manipulate widgets in the main thread. zentara |
#!/usr/bin/perl use warnings; use strict; use threads; use threads::shared; use Glib qw/TRUE FALSE/; use Gtk2 qw/-init -threads-init/; #setup shared hash my %shash; share(%shash); #will work for first level keys $shash{'go'} = 0; $shash{'work'} = ''; $shash{'die'} = 0; my $window = Gtk2::Window->new('toplevel'); $window ->signal_connect( 'destroy' => \&delete_event ); $window->set_border_width(10); $window->set_size_request(300,300); my $vbox = Gtk2::VBox->new( FALSE, 6 ); $window->add($vbox); $vbox->set_border_width(2); my $hbox= Gtk2::HBox->new( FALSE, 6 ); my $hbox1 = Gtk2::HBox->new( FALSE, 6 ); $vbox->pack_end($hbox,FALSE,FALSE,0); $vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0); $vbox->pack_end($hbox1,FALSE,FALSE,0); $hbox->set_border_width(2); $vbox->pack_end (Gtk2::HSeparator->new, FALSE, FALSE, 0); my $ebutton = Gtk2::Button->new_from_stock('gtk-quit'); $hbox->pack_end( $ebutton, FALSE, FALSE, 0 ); $ebutton->signal_connect( clicked => \&delete_event ); my $pbar = Gtk2::ProgressBar->new(); $pbar->set_pulse_step(.1); $hbox->pack_start($pbar,1,1,0); my $count = 0; my $label_w_markup = Gtk2::Label->new(); $label_w_markup->set_markup("<span foreground=\"yellow1\" size=\"40000\">$count</span>"); $vbox->pack_end($label_w_markup,FALSE,FALSE,4); my $tbutton = Gtk2::Button->new_with_label('Run Thread'); $hbox1->pack_start($tbutton , 1, 1, 0 ); my $lconnect = $tbutton->signal_connect( clicked => sub{ launch() }); my $sconnect; $window->show_all(); $pbar->hide; #needs to be called after show_all #create 1 sleeping thread passing it the label and pbar to control my $thread = threads->new(\&work, $label_w_markup, $pbar); Gtk2->main; ###################################### sub delete_event { $shash{'go'} = 0; $shash{'die'} = 1; $thread->join; Gtk2->main_quit; return FALSE; } ####################################### sub launch{ $pbar->show; $tbutton->set_label('Stop Thread'); $tbutton->signal_handler_block($lconnect); $sconnect = $tbutton->signal_connect( clicked => sub{ stop() }); $shash{'go'} = 1; } ################################################## sub stop{ print "stopped\n"; $shash{'go'} = 0; $pbar->hide; $tbutton->set_label('Run Thread'); $tbutton->signal_handler_block ($sconnect); $tbutton->signal_handler_unblock ($lconnect); } ######################################################### sub work{ my ($label,$pbar) = @_; $|++; while(1){ if($shash{'die'} == 1){ goto END }; if ( $shash{'go'} == 1 ){ foreach my $num (1..1000){ Glib::Idle->add( sub{ if($shash{'die'} == 1){ return }; $label->set_markup("<span foreground=\"yellow1\" size=\"40000\">$num</span>"); $pbar->pulse; return FALSE; }); select(undef,undef,undef, .1); if($shash{'go'} == 0){last} if($shash{'die'} == 1){ goto END }; } $shash{'go'} = 0; #turn off self before returning }else { select(undef,undef,undef,.1) } #sleep time } END: }
#!/usr/bin/perl # # $Header: /cvsroot/gtk2-perl/gtk2-perl-xs/Gtk2/examples/thread_usage.pl,v 1.4 2004/06/23 01:00:15 rwmcfa1 Exp $ # # -rm # use strict; use warnings; use Data::Dumper; use Glib qw(TRUE FALSE); use Gtk2 qw/-init -threads-init 1.050/; die "Glib::Object thread safetly failed" unless Glib::Object->set_threadsafe (TRUE); my $win = Gtk2::Window->new; $win->signal_connect (destroy => sub { Gtk2->main_quit; }); $win->set_title ($0); $win->set_border_width (6); $win->set_default_size (640, 480); my $hbox = Gtk2::HBox->new (FALSE, 6); $win->add ($hbox); my $vbox = Gtk2::VBox->new (FALSE, 6); $hbox->pack_start ($vbox, FALSE, FALSE, 0); my $worklog = Log->new; $hbox->pack_start ($worklog, TRUE, TRUE, 0); my @workers; my $worker; foreach (1..5) { $worker = Worker->new ($worklog); $vbox->pack_start ($worker, FALSE, FALSE, 0); $worker->set_worker_label ('Worker '.$_); push @workers, $worker; } my $pending = Gtk2::Label->new ('0 jobs pending'); $vbox->pack_start ($pending, FALSE, FALSE, 0); Glib::Timeout->add (500, sub { $pending->set_text (Worker->jobs_pending.' jobs pending'); 1; }); my $count = 0; my $go = Gtk2::Button->new ('_Go'); $vbox->pack_start ($go, FALSE, FALSE, 0); $go->signal_connect (clicked => sub { foreach (@workers) { Worker->do_job ($count + rand); $count++; } }); my $quit = Gtk2::Button->new_from_stock ('gtk-quit'); $vbox->pack_start ($quit, FALSE, FALSE, 0); $quit->signal_connect (clicked => sub { $go->set_sensitive (FALSE); $quit->set_sensitive (FALSE); Worker->all_fired; Gtk2->main_quit; }); $win->show_all; Gtk2->main; package Worker; use strict; use warnings; use Data::Dumper; use threads; use threads::shared; use Thread::Queue; use Glib qw(TRUE FALSE); use base 'Gtk2::HBox'; our $_nworkers : shared = 0; my $_jobs; BEGIN { $_jobs = Thread::Queue->new; } sub do_job { shift; # class $_jobs->enqueue (shift); } sub all_fired { shift; # class # put on a quit command for each worker foreach (1..$_nworkers) { $_jobs->enqueue (undef); } while ($_nworkers) { Gtk2->main_iteration; } } sub jobs_pending { return $_jobs->pending; } sub new { my $class = shift; my $worklog = shift; my $self = Gtk2::HBox->new (FALSE, 6); # rebless to a worker bless $self, $class; # gui section my $label = Gtk2::Label->new ('Worker:'); $self->pack_start ($label, FALSE, FALSE, 0); my $progress = Gtk2::ProgressBar->new; $self->pack_start ($progress, FALSE, FALSE, 0); $progress->set_text ('Idle'); $self->{label} = $label; $self->{progress} = $progress; $self->{worklog} = $worklog; # thread section $self->{child} = threads->new (\&_worker_thread, $self); $_nworkers++; return $self; } sub set_worker_label { my $self = shift; my $name = shift; $self->{label}->set_text ($name); } sub _worker_thread { my $self = shift; my $progress = $self->{progress}; my $worklog = $self->{worklog}; my $i; my $job; my $sleep; # undef job means quit while (defined ($job = $_jobs->dequeue)) { $worklog->insert_msg ($self->{label}->get_text ." is doing job ($job)\n"); if (rand > 0.5) { $sleep = 1 + rand; } else { $sleep = 1 - rand; } for ($i = 0; $i < 1.1; $i += 0.25) { Gtk2::Gdk::Threads->enter; $progress->set_fraction ($i); $progress->set_text ($i * 100 .'%'); Gtk2::Gdk::Threads->leave; # we're state employee's, so let's do some 'work'... sleep $sleep; } $worklog->insert_msg ($self->{label}->get_text ." done with job ($job)\n"); } $_nworkers--; } package Log; use strict; use warnings; use Glib qw(TRUE FALSE); use base 'Gtk2::ScrolledWindow'; sub new { my $class = shift; my $self = Gtk2::ScrolledWindow->new; my $buffer = Gtk2::TextBuffer->new; my $view = Gtk2::TextView->new_with_buffer ($buffer); $self->add ($view); $view->set (editable => FALSE, cursor_visible => FALSE); $self->{view} = $view; $self->{buffer} = $buffer; bless $self, $class; $self->insert_msg ("Start...\n-------------------------------------\n"); return $self; } sub insert_msg { my $self = shift; my $msg = shift; my $buffer = $self->{buffer}; Gtk2::Gdk::Threads->enter; my $iter = $buffer->get_end_iter; $buffer->insert ($iter, $msg); $iter = $buffer->get_end_iter; $self->{view}->scroll_to_iter ($iter, 0.0, FALSE, 0.0, 0.0); Gtk2::Gdk::Threads->leave; }
Attachment:
signature.asc
Description: OpenPGP digital signature