All,
Below are two examples of threaded win32-gui programmes that use Rob's new
extension. I have included them here to show how easy and powerful threaded
programming will become in new versions of Win32-GUI. Hopefully they will
stimulate some debate, which will make things even better:) Both examples
are included within Rob's zip:
http://www.robmay.me.uk/win32gui/
The first is a classic example of a boss-crew thread model - where one
thread is the boss, and the other threads the workers. The boss dishes out
work to the workers, and when the workers have finished they tell the boss.
In win32-gui this will be a very common situation, as the worker(s) will be
doing time consuming tasks (such as generating reports, database queries
etc) and you don't want to "freeze" the main window as you are waiting for
the worker to complete. Using the boss-worker/boss-crew is also the natural
way to add a GUI interface to a long running stand alone script.
The second example is a little more esoteric:) It creates a main window
running in the main thread. It then spawns several threads with each thread
running it's own win32-gui window. You can then interact with the spawned
threads, which send messages back to the main window. The use of this
threading style will not be that common in win32-gui, but is extremely
powerful - for example, it could be used to create object instances of
windows, with each window encapsulated within it's own object, running in
it's own thread.
#!perl -w
use strict;
use warnings;
use threads;
use Win32::GUI();
use Win32::GUI::ThreadUtils;
$|=1;
#Create our Boss window
my $mw = Win32::GUI::Window->new(
-title => 'Threads example - boss crew',
-pos => [100,100],
-size => [300,200],
-onTerminate => \&cleanup,
);
$mw->AddRichEdit(
-name => 'Report',
-width => $mw->ScaleWidth(),
-height => 130,
-multiline => 1,
-vscroll => 1,
);
$mw->AddButton(
-text => 'Send 1000 jobs',
-pos => [114, 140],
-height => 20,
-onClick => \&SendJobs,
);
#Create a message queue that is attached to the richedit control, when items
are added
#to the queue by worker threads, an "event" is fired.
my $comms = $mw->Report->AttachComms(\&report) or die "Failed to create
comms object";#
#Create a queue that is used to communicate to the workers
my $workqueue=Thread::Queue->new();
my $jobcount=1;
#How many working threads
my $numberOfWorkers=10;
#create the worker threads, passing the queues
for (1..$numberOfWorkers) {
threads->create(\&Worker,$workqueue,$comms);
}
$mw->Show();
Win32::GUI::Dialog();
exit(0);
sub SendJobs {
#send a thousand jobs
for (1..1000) {
$workqueue->enqueue($jobcount++);
}
}
sub report {
my ($self,$tid, $job)[EMAIL PROTECTED];
#print "Boss: Thread $tid processed job $item\n";
$self->SetSel(-1,-1);
$self->ReplaceSel("Thread $tid processed job $job\r\n");
}
sub cleanup {
for (1..$numberOfWorkers) {
$workqueue->enqueue(-1);
}
# Loop and join all the worker threads
# To avoid warnings like 'exited while N threads still running
foreach my $thr (threads->list) {
# Don't join the main thread or ourselves
if ($thr->tid && !threads::equal($thr, threads->self)) {
$thr->join;
}
}
return -1;
}
sub Worker {
my ($queue,$commsobj)[EMAIL PROTECTED];
print "Thread ".threads->tid(). " created\n";
#block on queue until we have some work to do
while (my $jobnum=$queue->dequeue()) {
# terminate thread:
last if $jobnum == -1;
#do some work for a random amount of time
Win32::Sleep(int(rand(25)));
#Send a message back to the boss to say that we're done
$commsobj->Call(threads->tid(), $jobnum);
}
print "Thread ".threads->tid(). " terminating\n";
}
#!perl -w
use strict;
use warnings;
$|=1;
#BUGS:
# No attempt is made to get the 'child' threads to terminate
# before the script exits, an so if any worer windows are not
# explicitly closed by clicking on the 'x' in their titlebar
# before the 'Main Window' is closed you will get
# 'A thread exited while N threads were running' warning
use threads;
use Win32::GUI();
use Win32::GUI::ThreadUtils;
sub CW_USEDEFAULT() {0x80000000};
my $numberOfWorkers=4;
my $main=MainWindow();
my $comms = $main->AttachComms(\&BossLog) or die "Failed to create comms
object";
for (1..$numberOfWorkers) {
threads->create(\&Worker, $_, $comms)->detach();
}
$main->Show();
Win32::GUI::Dialog();
sub MainWindow {
my $mw = Win32::GUI::Window->new(
-title => "Main window",
-left => CW_USEDEFAULT,
-size => [300,200],
);
$mw->AddTextfield (
-name => 'Log',
-width => $mw->ScaleWidth(),
-height => $mw->ScaleHeight(),
-multiline => 1,
-vscroll => 1,
);
return $mw;
}
sub BossLog {
my ($self,$message)[EMAIL PROTECTED];
$self->Log->SetSel(-1,-1);
$self->Log->ReplaceSel("$message\r\n");
}
sub Worker {
my ($number,$commsobj)[EMAIL PROTECTED];
my $mw = Win32::GUI::Window->new(
-title => "Thread $number window",
-left => CW_USEDEFAULT,
-size => [300,200],
);
#save the comms object
$mw->UserData($commsobj);
$mw->AddButton(
-text => 'Do some work in this window',
-pos => [114, 10],
-height => 20,
-onClick => \&Job,
);
$mw->AddTextfield (
-name => 'Process',
-size => [35,20],
-pos => [2,20],
);
$mw->Process->Text($number);
$mw->Show();
Win32::GUI::Dialog();
}
sub Job {
my $self=shift;
my $parent=$self->GetParent();
my $id=threads->tid();
Win32::Sleep(50);
my $item=$parent->Process->Text();
$parent->UserData->Call("Processed $item in thread $id");
}