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

So I have found my self in a dilemma, I would like to be able to run the following while loops concurrently.

while(1) { if(-e $templog) { print addtime("Found $templog!\n"); DB_Update_Stats(); CheckTS(); } else { print addtime("Could not find $templog!\n"); CheckTS(); } sleep $config{Database}->{Sleep}; } while(1) { DB_Backup(); sleep 7200; #Backup db every 2 hours }

What do you guys think?

Replies are listed 'Best First'.
Re: Concurrent Whiles?
by Athanasius (Archbishop) on Sep 08, 2013 at 03:00 UTC

    When you need two (or more) concurrent loops like this, you can use threads. However, in this case you can just combine them into a single loop, as follows (untested):

    my $time_slept = 0; while (1) { ... $time_slept += sleep $config{Database}->{Sleep}; if ($time_slept >= 7200) { DB_Backup(); $time_slept = 0; } }

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      Yes, that does help. Thank you.

Re: Concurrent Whiles?
by rjt (Curate) on Sep 08, 2013 at 03:24 UTC

    Your first while loop is a busy-wait loop, which is usually undesirable (definitely undesirable in this case). It would make sense to choose an interval that fits your problem for how often you need to check for $templog, and only poll that often. Even a delay of 1 second would greatly reduce the I/O and CPU load, but if you can live with longer, great.

    Better yet is to avoid polling altogether. What creates the file named $templog in the first place? Instead of using a flag file like this, can you use some kind of inter-process communication, or perhaps does it even make sense to just call DB_Update_Stats() at the time? These are questions you'd have to answer yourself, because there isn't enough information about your program here for me to confidently make that determination.

    Also, sleeping for 2 hours is risky; you might end up doing DB_Backup() more often than you expect, if your process is restarted or receives a signal. Are you on a system that has a task scheduler like cron or the Windows task scheduler? Those tend to be much more robust than a do-it-yourself solution, plus they only consume resources when they're running, not the 1 hour and 59 minutes in between. Again, you may have good reason to want to do it this way, but it may be worth considering.

    Once you've considered the above paragraphs, then you can look at the "how" of combining the two loops. In a case like this, you can decide how often each thing is supposed to happen (checking for $templog, and doing your DB backup), but sleep for a much shorter interval and compare time to see how long it's been. Here's one way you might do that, using a dispatch table (maybe more than you need, but if your needs expand later you'll be glad you know how):

    my %tasks = ( templog => { interval => 5, # seconds code => sub { if (-e $templog) { ... } else { ... } }, }, backup => { interval => 2 * 60 * 60, # 2h code => \&DB_Backup, }, ); while (1) { for (keys %tasks) { my $since = time - ($tasks{$_}{last} || time); if ($since >= $tasks{$_}{interval}) { $tasks{$_}{code}->(); $tasks{$_}{last} = time; CheckTS(); # Or put this in each sub { } } } sleep 1; # See note }

    Note: With a little algebra, you can calculate the exact length of time to sleep until the next event hits, but in practice, checking a small hash once per second is nothing.

    Hopefully this will get you started in the right direction.

    use strict; use warnings; omitted for brevity.
Re: Concurrent Whiles?
by space_monk (Chaplain) on Sep 08, 2013 at 10:24 UTC
    Normally I'm all in favour of Perl solutions, but doing something every 2 hours is something that really calls out for a cron process or whatever the Windows equivalent is.
    If you spot any bugs in my solutions, it's because I've deliberately left them in as an exercise for the reader! :-)