2

I am fairly new to Perl and I am trying to achieve the following:

sub foo {
    my $serviceRespone = makeSomeServiceCall();
    doSomeExpensiveOperationWhoseResultWeDontCareAbout($serviceResponse); # Should not block execution and run in background
    return $serviceResponse;
}

Is there any built-in functions and/or libraries that can help me fire-and-forget an action and improve latency, like the line I added the comment with? Thanks in advance!

David Chou
  • 23
  • 3
  • 1
    Does this answer your question? [Non-blocking / Asynchronous Execution in Perl](https://stackoverflow.com/questions/8548224/non-blocking-asynchronous-execution-in-perl) – Jesse Feb 02 '20 at 02:32
  • 1
    "Yes" would be your answer. For the most basic way, which I find wholly satisfactory, see [fork](https://perldoc.perl.org/functions/fork.html) (and you can double-fork for fire-and-forget). Take [perlipc](https://perldoc.perl.org/perlipc.html) to go with it. Then there are other ways, depending on what precisely you need, and there are most excellent modules. – zdim Feb 02 '20 at 04:58

3 Answers3

3

I use following piece of code to run tasks in parallel asynchronously

#!/usr/bin/perl
#
# USAGE:
#   prog.pl
#
# Description:
#   Demonstration how to run asynchronous processes
#   in parallel (threaded application)
#
# StackOverflow: 
#   Question 60022665
#
# Author:
#   Polar Bear      https://stackoverflow.com/users/12313309/polar-bear
#
# Date: Tue Feb 1 19:55:00 PST 2020
#

use strict;
use warnings;
use feature 'say';

use POSIX ":sys_wait_h";                # WNOHANG definition for waitpid
use Time::HiRes qw(usleep);             # We will use usleep when all workers busy

my $tasks   = 1000;                     # total number of tasks to run
my $workers = 5;                        # number of available workers

sub REAPER {                            # $SIG{CHLD} exit handler
    while ((my $kid = waitpid(-1, &WNOHANG)) > 0) {  #  to avoid zombie
        say "Process: $kid done";       # worker finished it's work
        $workers++;                     # increment number of available workers
    } 

    $SIG{CHLD} = \&REAPER;              # Attach $SIG{CHLD} to handler
}

$SIG{CHLD} = \&REAPER;                  # Attach $SIG{CHLD} to handler

while( $tasks ) {                       # while there are tasks to run

    while( $workers == 0 ) { usleep(1) } # while all workers busy take a nap

    my $pid = fork();

    die "cannot fork" unless defined $pid;

    if( $pid == 0 ) {                   # child process
        worker($arg);                   # worker started
        exit;                           # IMPORTANT: worker must exit to signal REAPER          
    } else {                            # parent process
        $tasks--;                       # decrement numbers of tasks to run
        $workers--;                     # decrement number of available workers
    }
}

exit 0;                                 # all processes has finished their work

sub worker {                            # worker process
    my $arg = shift;

    say "Started process $$ with argument $arg";

    sleep( rand(20) );                  # simulate process work
}

NOTE: please read documentation for fork and waitpid about limits and portability. See documentation perlipc for more examples.

Polar Bear
  • 6,762
  • 1
  • 5
  • 12
2

If you don't care about the result of the long-running operation, builtin fork is sufficient

if (fork() == 0) {
    # child process
    doSomeExpensiveOperationWhoseResultWeDontCareAbout($serviceResponse);
    exit;
}
# parent process continues ...
mob
  • 117,087
  • 18
  • 149
  • 283
  • And check with `waitpid`, at parent's end for example, for children that may have exited (or use `$SIG{CHLD} = 'IGNORE';` if suitable) – zdim Feb 04 '20 at 05:55
  • (and then that's all they need for the stated purpose, I meant to say) – zdim Feb 04 '20 at 07:11
1

You can also use threads:

    use threads;

    sub expensiveop {
        [...]
    }

    sub foo {
        my $thr = threads->create(\&expensiveop);
        my $serviceRespone = makeSomeServiceCall();
        return $serviceResponse;
    }

    [...]
    $thr->join(); # wait for thread to finish if you care
  • Thank you for this answer! Can you help out those of us seeing this by providing a little more explanation as to why we would want to use threads and what this code does exactly? Comments would help newer users understand the code sample! – RedBassett Feb 12 '20 at 19:19