http://qs321.pair.com?node_id=741711

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

Hello again perl masters!

I hope I'm not annoying you all with all my newbie questions!

I'm wondering what the best method is for running multiple simultaneus background functions while having a gtk gui to control them would be.

So far I've been using timeouts (because I was having issues with a module I wrote that used threads causing Gtk to spit out very non-helpful errors) and I've noticed the gui seems to lag while the timeout functions are running.

Would threads be a better solution? Might I be failing to do something properly in regards to the timeouts that could be causing this? I've basically been learning how to do this by reading the Gtk2 pods and lots of trial and error. That being said it's very possible the way I'm going about doing this is pretty nasty.

I'd post the code but I don't think any of you would be willing to read something THAT long ;-) and this is more of a programming theory question than a bug hunting problem anyway.

My guess is threads would probably be the better option and I should just use timeouts when necessary to start up the threads that do the actual work.

Thoughts?

Thanks!

  • Comment on Gtk2 app -- what's better, threads, or multiple timeouts?

Replies are listed 'Best First'.
Re: Gtk2 app -- what's better, threads, or multiple timeouts?
by BrowserUk (Patriarch) on Feb 05, 2009 at 23:26 UTC
    I've noticed the gui seems to lag while the timeout functions are running.

    That's the problem with cooperative multitasking (event loops).

    You have to balance the needs of being responsive to interactive routines and getting work done in cpu intensive routines. Manually. By either breaking the cpu intensive bit up into iddy biddy chunks or interspersing your loops with yeilds. And just when you get the balance right on your machine, you'll have to re-tune it to run on a faster machine, or a slower machine. Or both.

    And just when you think you've got that cracked, someone will call for the addition of another background task and you'll have to do the balancing act all over again. And when you've cracked that and the code goes into the field, although the users are running teh same hardware as you, they are also running another cpu-intensive program concurrently that saps half of the available cycles. That means your carefully partitioned loops now take twice as long as they do on your machine and once again, the gui becomes sluggish.

    Pre-emptive multitasking (threads) does not suffer these problems. Whatever hardware you run it on, and whatever the current cpu load, except for in extreme circumstances, the OS scheduler will ensure that your gui gets sufficient and timely slices of the cpu to remain responsive. That's why threads were invented.


    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.
      I had to read that a few times but I think I get it. Do you mean Pre-emptive multitasking (threads) does NOT suffer these problems.?

      So basically I could mess around trying to tune all the loops to happily co-exist and do it all again when anything changes, or just use threads because this is what they are intended for.

      I sure wish there was a document somewhere that explained all these concepts. This is really my first attempt at any real event-driven application. I think I'm beginning to understand though.

        Do you mean

        Yes. Now corrected. Sorry.

        So basically I could mess around trying to tune all the loops to happily co-exist and do it all again when anything changes, or just use threads because this is what they are intended for.

        Ostensibly yes. As always, reality is usually a bit more complicated. Because many GUI toolkits are designed to be used as single threaded, cooperative, event-driven state machines, there is no provision within the toolkit for adding events to their event queues from other threads. Which makes adding stuff (results) to the gui from other threads a tad more awkward than you would like it to be. The solution is to use a hybrid architecture whereby the threads post results and updates for the gui to a Thread::Queue, and you use a timer in the gui to regularly check (poll) that queue from the main (gui) thread looking for thos updates.

        I never did get GTK2 to work with perl on my (win32) system (though it runs fine here from Ocaml), so I cannot offer you a sample, but I'm fairly sure that zentara has posted some GTK2 + threads snippets, and we've both posted Tk samples that demonstrate the techiques.


        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: Gtk2 app -- what's better, threads, or multiple timeouts?
by traveler (Parson) on Feb 05, 2009 at 23:01 UTC
    I have had such great success using timeouts and events with Gtk2 that I have not bothered with threads. If you are experiencing lag, then either 1) run the main loop occasionally during cpu-intensive functions, 2) shorten the timeout time, or 3) allow other events (e.g. data ready on a pipe) to cause a different function to run.

    I did a many-thousand line system this way and it ran very smoothly. For me the trick was when to allow timeouts and when to block them due to shared resources/data structures. In my case, I seldom used actual timeouts for swithching between functions. I wrote subs that ran for a very short amount of time that were triggered by internal or external events. The timers mostly served to cause screen synchronization with actual data, update the status of other nodes (in a cluster) and prevent the system from hanging when, say, an expected event such as a user input took some time.

    One interesting thing to consider: if you are displaying a large amount of data, consider how often it must be updated and only compute values when they need to be displayed. That is, if you ahve a rapidly changing value for $foo, don't compute the median $foo until you need to show it (unless, of course, you need to do something else with it). This saves time and hence makes the system more responsive.

    HTH,
    --traveler

      If you are experiencing lag, then either 1) run the main loop occasionally during cpu-intensive functions

      Okay I'm not sure I understand this. I was under the impression that the main loop was always running. Does this mean that when I run a timeout, the main loop "waits" for whatever the timeout does to finish before it continues on? Observing my program it does seem like that is the case.

      Each run of the timeout in my program polls a certain website for changes, parses the new data (if any) and if there's something new it displays the data in the gui. So each run of the timeout has to wait for the webpage to be fetched which is where the slowdown is coming from.

      If I'm understanding how the main loop works, I think I'd like to have the main loop not wait for anything the program does in the background, but again I'm not sure if that's the right way to solve the problem. (Update: I should clarify that what I mean by "anything the program does in the background" is anything like the polling of the website that is not triggered by any gui events, it just does that all the time. In other words I want these background functions to be a seperate operation that doesn't effect the gui (unless there's something new to be displayed). Hopefully I'm making sense here ;-)

        The golden rule of event driven code is "never wait". Often that means that you need to initiate a process (start the fetch) then return and handle the reply some time in the future. One thing that implies is that you can only make non-blocking calls in the event processing.


        Perl's payment curve coincides with its learning curve.
        If you are experiencing lag, then either 1) run the main loop occasionally during cpu-intensive functions

        Okay I'm not sure I understand this. I was under the impression that the main loop was always running. Does this mean that when I run a timeout, the main loop "waits" for whatever the timeout does to finish before it continues on? Observing my program it does seem like that is the case.

        The main loop waits for events. When it gets one it processes it. If the event it is processing is a timer event tied to a sub of yours, it runs the sub. (Don't) try this

        while (1) { ... }
        It will hang the GUI. So in an event-driven environment such as Gtk2 you allow events to trigger subs. Never have your code sit around waiting for something to happen, that is the main loop's job. When something happens it will trigger an event (if you ask it to) and run your sub. The only time you should ask Gtk2 to process pending events if if you have a compute intensive block of code. For instance, if you are sorting a few zillion items, every once in a while (pun intended) try
        Gtk2->main_iteration while Gtk2->events_pending;
        This runs the main iteration code (which is what loops in the main loop while there are any events pending. Of course, you need to make sure that this does not mangle the data you are processing.
Re: Gtk2 app -- what's better, threads, or multiple timeouts?
by zentara (Archbishop) on Feb 06, 2009 at 18:40 UTC
    See Threads-w-Perl/Gtk2 demo. One thing of importance to mention, is that you will see examples of Gtk2 thread programs with threads->enter and threads->leave, for thread isolation control. This is to allow accessing the Gtk2 widgets, from the threads without causing segfaults. Enter/leave works, but the conventional wisdom now, is to use Glib::Idle->add() from the threads to access external Gtk2 widgets. Here is a demo

    I'm not really a human, but I play one on earth Remember How Lucky You Are
      Oops I just got around to checking that out.
      I'm going to have to agree with the "conventional wisdom" on this one -- this definitely seems like the most reasonable way to go about updating the gui from a thread given the options.

      Thank you very much!