Sixtease has asked for the wisdom of the Perl Monks concerning the following question:

Hello friends,

I'm making a Tk-based application, where I want the main window to be responsive, while its parts are being filled with images. I thought I'd fill the appropriate frame with the images in a separate thread but I read that it is better to not share objects.

So, I thought I'd prepare the image data in a separete thread, fill it to a shared scalar variable and then somewhow let the main thread know that it can add the image to the frame.

And here is the problem -- how to let the main thread know? In documentation about threads, I read how to wait for a thread to finish but that's exactly what I do not want to do - I want the main window to do its MainLoop, not wait, that's why I'm using threads. :-) I tried sending signals to myself (kill 'USR1', $$) but that doesn't seem to work well. Subsequent signals are not delivered or cause a segfault when they come faster than they are being dispatched.

I'm sure I'm not in a new situation... I'm probably missing something obvious. Could you direct me?

~ Sixtease

Replies are listed 'Best First'.
Re: How to not wait for a thread
by renodino (Curate) on Oct 25, 2007 at 19:33 UTC
    Perl/Tk is not threads-friendly. Its not exactly threads-hostile, but you need to make sure you run Perl/Tk in its own thread, and don't spawn any new threads from the same thread as Perl/Tk runs in.

    That means you can't just rely on MainLoop magically stepping aside to check on your other threads, nor can you fiddle with Tk's structures from an external thread. You'll need to add a repeat timer event before calling MainLoop, and use queues to pass commands/data back and forth.

    Here's some (untested) code snippets to get you going.

    #!/usr/bin/perl -w use threads; use Thread::Queue; use Tk; use strict; my $cmdq = Thread::Queue->new(); my $respq = Thread::Queue->new(); my $thrd = threads->create(\&image_loader); my $mw = MainWindow->new; # ...the usual widget building here... my $repeat_id = $mw->repeat($interval, \&image_handler); $cmdq->enqueue('GO'); MainLoop; sub image_handler { return unless $respq->pending; my $buffer = $respq->dequeue(); ...load into your frame here... $cmdq->enqueue('GO'); } # # assumes your image loader persists # sub image_loader { while (1) { my $go = $cmdq->dequeue(); last if ($go eq 'STOP'); ...do your image loading business here... $respq->enqueue($imagebuffers); } }

    Perl Contrarian & SQL fanboy

      OK thanks a lot guys.

      So I need to use active waiting (timer). I was hoping for a passive waiting method (get a signal or sth) but this seems OK. I'll do as you advise. Thank you once again.

Re: How to not wait for a thread
by zentara (Cardinal) on Oct 25, 2007 at 19:35 UTC
    Tk makes it easy for you to do that, because it is an event-loop system. So you can start a timer, which checks for a shared variable every 10 ms (or whatever update time you need). You didn't show your code, but you probably want to reuse your threads, so you don't get a memory gain.

    So the basic idea is to make your threads, before you invoke any Tk code, see PerlTk on a thread... for other precautions. Then setup your Tk gui. Then use shared variables to signal back and forth between the Tk in the main thread and the worker thread(s). To detect when a thread is done processing an image, have a shared var called for instance "$done", and have a timer in the main Tk thread, check $done every 10 ms. When $done == 1; transfer the image thru another shared var into the Tk main thread. Since Tk likes it's images to be base64encoded anyways, have the thread base64encode the image, before it puts it into the output shared var. See also Tk-with-worker-threads for an example of making reusable threads that sleep when not used. This technique prevents any increase in memory caused by continually creating and destroying threads.( You cannot create/destroy threads with Tk anyways, since Tk is not thread-safe, and you need to create ALL threads before any Tk code is invoked in the main thread).


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum