Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Do I need threading for my GUI?

by tamaguchi (Pilgrim)
on Feb 18, 2008 at 09:19 UTC ( #668492=perlquestion: print w/ replies, xml ) Need Help??
tamaguchi has asked for the wisdom of the Perl Monks concerning the following question:

I have a -tk GUI with various settings. After the settings have been set the user can start the "mainprocess" with the startbutton. The "mainprocess" however is processor-power and timeconsuming. So during the run of the "mainprocess" the gui freezes and doesnt update. It looks very bad. Do you know of any tricks to use to avoid this, or do I have to use threading, (which I know nothing about in perl)? Thank you for any help or suggestions.

Comment on Do I need threading for my GUI?
Re: Do I need threading for my GUI?
by Joost (Canon) on Feb 18, 2008 at 09:47 UTC
    Perl threading is not exactly without its bugs and caveats. The simplest solution is probably to split the main process up in short bits that schedule the next bit via Tk::after.

    For instance:

    my @long_list_of_items; # do not call this directly sub main_process { if (@long_list_of_items) { process_item(shift @long_list_of_items); $button->after(50,\&main_process); } } # call this to add items to the list sub add_item { my $schedule = !@long_list_of_items; push @long_list_of_items,@_; if ($schedule) { $button->after(50,\&main_process); } }
    Update: slight optimization of scheduling.
Re: Do I need threading for my GUI?
by weismat (Friar) on Feb 18, 2008 at 10:05 UTC
    I think that perl threading works better than its reputation - the main drawback is that you can not share references/objects.
    If you start to use threads, than I would suggest you to work with the Thread::Queue for anything which is more complex for synchronisation - consider to work with polling in the beginning.
    Threading will become only complex if you need to display the results in the TK window.

      Whilst the default is to not share data between threads variables can be declared as shared, see Threads and Data for examples.

Re: Do I need threading for my GUI?
by jrtayloriv (Pilgrim) on Feb 18, 2008 at 11:06 UTC
    It seems to me that using POE would be a better idea than trying to do this with threads.
Re: Do I need threading for my GUI?
by shmem (Canon) on Feb 18, 2008 at 11:06 UTC
    A trick to avoid this is giving back control to the Tk main loop. This can either be done by splitting up the "mainprocess" into singular tasks which call each other e.g. via Tk->after, or storing that main-processes state somehow, thus making it re-entrant. You could also use the POE framework to do either of those.

    Term::ReadLine::Gnu uses another trick, it calls the Tk event handler DoOneEvent() directly - with due care, because it blocks if there's no event dispatched. If the above techniques (or just the use of POE) aren't feasible, you could use that trick.

    See Tk::Event.

    update:

    The proffered solution... Clock that goes backwards enhanced with a CPU-intensive, long running task. While the task is as silly as the clock itself, it is suitable for the POC.

    It's just a matter of adding a signal handler and calling the event dispatcher in a non-blocking way.

    #!/usr/bin/perl use strict; use Time::HiRes qw(ualarm); use Tk; BEGIN {*Tk::Clock::time = sub { time * -1 } } use Tk::Clock; my $wm = MainWindow->new; my $ck = $wm->Clock->pack(-side => 'top'); my $b = $wm->Button(-text => 'Start Process', -command => \&mainproce +ss)->pack; $ck->config(dateFormat => "dd-mm-yyy"); MainLoop; # # simulate a long running, CPU-intensive task # sub mainprocess { local $SIG{ALRM} = sub { DoOneEvent(Tk::Event::DONT_WAIT); ualarm(1000); } ualarm(1000); # task body begin my $c; $b->configure(-text => 'running...'); while (1) { $c++; print $c,"\n" unless $c % 1000; last if $c == 10000000; } $b->configure(-text => 'Start Process'); # task body end ualarm(0); }

    Pretty few lines of code added... well, I could shoehorn the alarm handler into one line, and it would be 3 lines - not counting the 'use Time::HiRes' line ;-)

    I guess it's not necessary to offer a code conversion job for something that simple.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Do I need threading for my GUI?
by BrowserUk (Pope) on Feb 18, 2008 at 14:03 UTC

    Forget what most of the people in this thread have said. Half of them have never used threads, and the other half have never tried their proffered solution in conjunction with Tk.

    Take it from someone who has used threads in conjunction with tk. For the scenario you describe, using a thread is by far the simplest solution. Indeed, if you will post (or email to me) the code for your current solution that locks the gui when the "mainprocess" runs, I will convert it to use threads and prevent that, with 3* lines of code.

    Let's see any of them offer to convert your existing code to their proffered solution?

    *It may take a few more lines to ensure that you can interrupt the main process, or terminate it early, cleanly, but in most cases, only a few more.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      My what big muscles you have :D
      You are going to show us how you do this, right? Please? A small sample program? Dave

        Sure:

        Existing script:

        ... use threads; ## Additional line 1 ... async{ ## Additional line 2 ## existing, long running, cpu intensive code here }; ## Additional line 3 ...

        Now the gui will be responsive, whilst the long running code runs full speed without changes.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Do I need threading for my GUI?
by zentara (Archbishop) on Feb 18, 2008 at 18:50 UTC
    I pretty much agree with BrowserUk, threads are simple and uncomplicated for doing your task. Tk with threads presents a bit of a roadblock, because the thread needs to be created before the Tk code is called, but it is easily done. The only thing not mentioned, is if you need feedback from/to the process as it runs. If so, threads are a good choice, BUT if you are just launching the process and letting it go off on it's own, a simple fork-and-exec may be cleaner.

    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
      Thank you all for your answers I did like this:
      my $mythread = new Thread \&MainEntry::StartProcessing;

      It looks at it was sufficient to solve the problem. Im not sure if the thread is alive after it has finished the StartProcessing sub however..I have tried to $mythread->detach; but it gives me a the error:

      "Attempt to free non-existent shared string '_TK_RESULT_', Perl interpreter: 0x1f28444 at C:/Perl/site/lib/Tk/Widget.pm line 98 during global destruction. Free to wrong pool 1f273d8 not 223f90 at C:/Perl/site/lib/Tk/Widget.pm line 98 during global destruction. "

      Does anyone have a clue what the problem might be?

        It means that one (or more) Tk variables was (unnecessarially and incorrectly) cloned from the main thread into the child thread when you spawned it. And when Perl attempts to clean it up (during global destruction), it doesn't know how. It can be safely ignored.

        If you only intend to start one (or a few) threads, you can defer the error until the program terminates by not detaching the threads. You will then receive a message along the lines of:

        Perl exited with active threads: 0 running and unjoined 1 finished and unjoined 0 running and detached

        Which again can be safely ignored (but unfortunately not turned off).

        As zentara pointed out elsewhere, it is possible to avoid these errors by starting the non-Tk threads prior to loading Tk so that the cloning doesn't happen, but unless you are intending starting enough threads for their dead corpses hanging around to become a (memory) problem, the extra complexity involved is usually not worth the effort.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
        This is probably the reason why many of us say "don't use threads for this task".

        Stick with the framework. It doesn't use threads and is event based? Don't use threads, use events, then. The problem can be solved within the same framework, without adding threads into the mix. Starting a long running sub prevents Tk from servicing other events? Make sure your sub cooperates and yields time slices to Tk.

        I'm not saying that it is a bad idea to use Tk within a threaded application, but that it is probably not so good an idea to use threads within Tk, since the framework itself is threads-unaware and probably not programmed in a thread-safe way; so using threads within it requires good understanding of the working of both perl threads and Tk.

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
        The real question is do you want to collect output from the processing in the thread? You don't say what the rest of the Tk gui is doing, like tailing a log file, or receiving and printing results? Is the command you are running in the thread, run thru system or backticks? See PerlTk on a thread... for tips on setting up a Tk thread. If you want to delay the start of the processing, until a button is pressed, try making a sleeping thread, which wakes up on a button press, as in Tk-with-worker-threads. The thing to notice with a sleeping thread, is you create it before any Tk is called, you put it in a sleep loop, waiting for a shared variable to change, which signals it to wake up and start processing.

        I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Do I need threading for my GUI?
by mattr (Curate) on Feb 19, 2008 at 05:28 UTC
    Don't know about Tk sorry but I do know threads are common in the wxperl gui library (wxwidgets). If you are interested in how that works (use threads;) you can look here. The idea being only one thread writes to the GUI. I myself experienced a similar thing as you did until I learned how to do the GUI right (in linux and windows).

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://668492]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (7)
As of 2014-11-29 03:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My preferred Perl binaries come from:














    Results (203 votes), past polls