Re: Multithreading



On Tuesday, November 4, 2003, at 06:51 PM, Jens Wilke wrote:

i tried to use threads, but it seems not to work together with gtk2-perl.
Even $widget1->add($widget2); causes error.
Are there any working eyamples out there?
I want to make a whole window and all it's stuff an own thread.
I've got Image::Magick operations "in that window" which makes the whole app
waiting.
Is there any other way in Gtk2 than forking?

Glib has a few measures taken towards thread safety: a) internal data structures are locked, and b) closures are invoked with the interpreter context that created them. Brett Kosinski contributed most of this, in order to get Glib to work with his GStreamer bindings which create processor threads in the background.

i imagine that the guidelines would be the same as with normal X apps in C, only mess with the gui from a single thread.

/me experiments a little

yeah, it looks that way. so long as you only create widgets *after* creating threads, it should work okay.

as i understand it:
perl's threads work by duplicating the interpreter.
that means that all existing objects are cloned when the thread is started.
when the thread exits, the objects get cleaned up.
however, the widgets point to only one thing behind the scenes.
so when the background thread tries to clean up, it destroys the widgets out from under the foreground thread, and badness ensues.

moral: don't do that.


(that said, we're looking into ways to keep things from blowing up if you create widgets before launching a thread, but perl's internals can be rather dense.)


that leaves you with the master and worker threads, communicating via work queues and all that fun stuff. according to the manpage for threads::shared, bless and threads don't get along, so you can't share blessed objects, and from experience i've seen that only basic types can be used as values in shared variables (strings, integers, doubles, and shared references). note that Data::Dumper and eval are handy for serializing objects into strings and thawing them out again.

-=-=-=-=-=-

use strict;
use warnings;
use threads;
use threads::shared;

my $deathflag : shared;
my @work_q : shared;

$deathflag = 0;
my $thread = threads->create (sub {
        while (! $deathflag) {
                if (@work_q) {
                        print "next: ".(shift @work_q)."\n";
                        sleep 1;
                } else {
                        threads->yield;
                }
        }
});

use Glib;
use Gtk2 '-init';

my $lastbusy = 0;
my $n = 0;

my $win = Gtk2::Window->new;
$win->signal_connect (delete_event => sub {
                if (@work_q) {
                        warn "can't quit, busy...\n";
                } else {
                        Gtk2->main_quit;
                        $deathflag = 1;
                }
                # either way, don't destroy the window -- we'll do that
                # by hand below.
                return 1;
        });
my $box = Gtk2::VBox->new;
$win->add ($box);
my $label = Gtk2::Label->new ('idle');
$box->add ($label);
my $button = Gtk2::Button->new ('queue some work');
$box->add ($button);
$button->signal_connect (clicked => sub {
        push @work_q, ++$n;
});

Glib::Idle->add (sub {
        # touch the queue only once to avoid race conditions.
        my $thisbusy = @work_q;
        if ($thisbusy != $lastbusy) {
                $label->set_text ($thisbusy ? "$thisbusy left" : 'idle');
                $lastbusy = $thisbusy;
        }
        1;
});
$win->show_all;
Gtk2->main;
$win->destroy;

$thread->join;

--
muppet <scott at asofyet dot org>




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]