Part of this just will not work.
I've tried ripping out the variables to live in the global space
(shudder) and everything...
I just get:
Subthread: Processing queue item /Applications/Poser 7/Runtime/kuyg.pz2
ST: working: /Applications/Poser 7/Runtime/kuyg.pz2
thread failed to start: lock can only be used on shared values at
PolymorphLayout.pl line 825.
Line 825 is the one that says:
$wip = Wx::PlThreadEvent->new(-1, $qWorkEVT, $pct_done);
I stuck the bareblock on just because, it doesn't matter. I don't
know. I'm just shooting in the dark now.
Up in the top I have it clerarly saying (outside of the constructor
and everything):
our $qWorkEVT :shared;
our $pct_done :shared;
$qWorkEVT = Wx::NewEventType;
$pct_done = 0;
sub process_queue {
my $self = shift;
my $e = shift;
@_ = ();
$self->{qthr} = threads->create(
sub {
# my $pct_done :shared;
$pct_done = 0;
#share($pct_done);
while (my $q = (grep $_->{status} eq 'queued',
@{$self->{queue}})[0]) {
my $wip;
warn "Subthread: Processing queue item $q->{outputFile}\n";
if (scalar(grep $_->{status} ne 'queued', @{$self->{queue}})) {
$pct_done = scalar @{$self->{queue}} /
(scalar(grep $_->{status} ne 'queued', @{$self->{queue}}) * 100);
}
else {
$pct_done = 0;
}
$q->{status} = 'working';
warn "ST: working: $q->{outputFile}\n";
# warn is_shared($qWorkEVT) ? "\$qWorkEVT is shared\n"
: "\$qWorkEVT is not shared.\n";
# warn is_shared($pct_done) ? "\$pct_done is shared\n"
: "\$pct_done is not shared.\n";
{
$wip = Wx::PlThreadEvent->new(-1, $qWorkEVT, $pct_done);
}
warn "ST: Past throwback\n";
Wx::PostEvent($self, $wip);
sleep 6;
$q->{status} = 'done';
if (scalar(grep $_->{status} ne 'queued', @{$self->{queue}})) {
$pct_done = scalar @{$self->{queue}} /
(scalar(grep $_->{status} ne 'queued', @{$self->{queue}}) * 100);
}
else {
$pct_done = 0;
}
{
$wip = Wx::PlThreadEvent->new(-1, $qWorkEVT, $pct_done);
}
Wx::PostEvent($self, $wip);
}
$pct_done = 100;
my $qdone = new Wx::PlThreadEvent(-1, $qWorkEVT, $pct_done);
Wx::PostEvent($self, $qdone);
}
);
}
2009/7/14 Dodger <[email protected]>:
> Hey all, especially Mattia,
>
> I'm working my way through threads in Wx and I'm looking into using
> threads to implement background queue processing. The user adds stuff
> to a queue, which is a part of the app object, and it processes a loop
> on the back end. Basically, I want them to be able to add to that
> queue while it's going, without having to pause it.
>
> I'm about to go to bed before delving into doing the real code, so I
> figured I should throw this up to see if I'm approaching things right,
> and see if there are any responses when I wake up.
>
> Basically, in simple form, I'm thinking this is the right approach,
> and I am about to try it. However, threads are tricky so even if it
> works, if someone sees this and screams NO THAT'S WRONG I need to
> listen. So, am I getting the approach right here?
>
> package Polymorph;
>
> use threads;
> use threads::shared;
> use Wx(':everything'); # I despise using 'qw' for one thing no matter
> how many things everywhere do it as an example
> use vars ('@ISA');
> @ISA = ('Wx::Frame');
>
> sub new {
> �...@_[3, 4, 5, 6] = (Polymorph', [-1, -1], [760, 610],
> wxMINIMIZE_BOX|wxMAXIMIZE_BOX|wxSYSTEM_MENU|wxCAPTION|wxCLOSE_BOX);
> my $self = shift->SUPER::new(@_);
>
> $self->{params} = {};
> $self->{queue} = &share([]);
> $self->{qWorkEVT} = Wx::NewEventType;
> &share($self->{qWorkEVT});
>
> Wx::Event::EVT_COMMAND($self, -1, $self->{qWorkEVT}, \&wip_queue_process);
>
> # all the other GUI setup stuff goes here, including buttons which
> trigger the events for subs below
> }
>
> sub add_to_queue { # add current to queue button pressed
> my $self = shift;
> my $e = shift;
>
> $self->{queue} ||= &share([]);
>
> my $q = &share({});
> for my $k (keys %{$self->{params}}) {
> $q->{$k} = $self->{params}->{$k};
> }
> $q->{status} = 'queued';
> push @{$self->{queue}}, $q;
> }
>
> sub process_queue { # start processing queue button pressed
> my $self = shift;
> my $e = shift;
> �...@_ = (); # do I need to do this if I shift in the vars? PBP is
> full of crap on this one, IMO. I always shift in $self, and $e is easy
> to do it with too.
>
> $self->{qthr} = threads->create(
> sub {
> my $pct_done : shared = 0;
> &share($pct_done);
>
> while (my $q = (grep $_->{status} eq 'queued',
> @{$self->{queue}})[0]) {
> # not a for loop, in case the queue changes in the meanwhile
> warn "processing queue item $q->{outputFile}\n";
> #warning while developing, since most of this will be quiet instead
> # do stuff to process the batch entry
> $q->{status} = 'done';
> $pct_done = scalar @{$self->{queue}} / (scalar(grep
> $_->{status} ne 'queued', @{$self->{queue}}) * 100);
> my $wip = new Wx::PlThreadEvent(-1, $self->{qWorkEVT},
> $pct_done);
> Wx::PostEvent($self, $wip);
> }
>
> $pct_done = 100;
> my $qdone = new Wx::PlThreadEvent(-1, $self->{qWorkEVT},
> $pct_done);
> Wx::PostEvent($self, $qdone);
> }
> );
> }
>
> sub wip_queue_process {
> my $self = shift;
> my $e = shift;
>
> my $pct_done = $e->GetData;
> # Update a gauge, maybe, or something... print to the status bar. Whatever.
>
> if ($pct_done == 100) {
> $self->finish_queue_process($e);
> }
> }
>
> sub finish_queue_process {
> my $self = shift;
> my $e = shift;
> warn "queue processing done.\n";
> $self->{qthr}->join; # the examples don't show this anywhere, but
> it's bad to not do this, right? Zombies, brain-eating, all that...
> # And isn't this the perfect time to catch it?
> }
>
>
> package main;
>
> unless (caller) {
> local *Wx::App::OnInit = sub {1};
>
> my $app = Wx::App->new();
> my $window = Polymorph->new();
>
> $app->SetTopWindow($window);
> $window->Show(1);
> $app->MainLoop();
> }
>
>
>
> BTW!
> On a side note, I noticed something funny looking in the docs.
>
> In the Wx threading pod, it says:
>
> "Sending events from worker threads
>
> Wx::PlThreadEvent can be used to communicate between worker and GUI
> threads. The event can carry a shared value between threads.
>
> my $DONE_EVENT : shared = Wx::NewEventType;
>
> sub work {
> # ... do some stuff
> my $progress = new Wx::PlThreadEvent( -1, $DONE_EVENT, $progress );
> Wx::PostEvent( $frame, $progress );
>
> # ... do stuff, create a shared $result value
> my $end = new Wx::PlThreadEvent( -1, $DONE_EVENT, $result );
> Wx::PostEvent( $frame, $end );
> }
> The target of the event can be any Wx::EvtHandler"
>
> However, there seems to be something wrong in that. Mainly in that
> there's a '$progress' variable declared in a my, but being dependant
> on already being defined in the same scope.
>
> I'm thinking something like this is what was meant, instead:
>
> "
> Sending events from worker threads
>
> Wx::PlThreadEvent can be used to communicate between worker and GUI
> threads. The event can carry a shared value between threads.
>
> my $DONE_EVENT : shared = Wx::NewEventType;
>
> sub work {
> # ... do some stuff, generating a shared $progress variable
> my $progress_so_far = Wx::PlThreadEvent->new(-1, $DONE_EVENT, $progress);
> Wx::PostEvent( $frame, $progress_so_far );
>
> # ... do stuff, create a shared $result value
> my $end = Wx::PlThreadEvent->new( -1, $DONE_EVENT, $result );
> Wx::PostEvent( $frame, $end );
> }
> The target of the event can be any Wx::EvtHandler
> "
>
> That avoids saying my $progress = new Wx::PlThreadEvent(-1,
> $DONE_EVENT, $progress);
> and thus avoids using $progress to define my $progress, because I'm
> pretty sure that's not right. I mean, unless you are defining
> $progress elsewhere.
>
> --
> Dodger
>
--
Dodger