David Chou
David Chou

Reputation: 23

Asynchronous programming in Perl

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!

Upvotes: 2

Views: 1042

Answers (4)

d2alphame
d2alphame

Reputation: 195

Here is how I would use the threads module for the code sample posted in your question:

use threads;     # The threads module will be needed

sub foo {
  my $serviceResponse = makeSomeServiceCall();

  # Here, the thread is created, the code ref of the code to run is passed in
  # in as the first parameter. All the remaining parameters are passed to the
  # ref'd code as parameters
  my $thrd = threads->create(
     \&doSomeExpensiveOperationWhoseResultWeDontCareAbout, $serviceResponse
  );

  # Detach the thread since we don't care about the result. Detached
  # threads will silently terminate when the program exits.
  $thrd->detach; 

  return $serviceResponse;
}


# Here, you define the function you don't want to wait for.
sub doSomeExpensiveOperationWhoseResultWeDontCareAbout {
  my ( $serviceResponse ) = @_;
  # Do stuff ...
}

Note that the threads module is built-in.

Upvotes: 0

Ruben de Groot
Ruben de Groot

Reputation: 11

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

Upvotes: 1

Polar Bear
Polar Bear

Reputation: 6798

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.

Upvotes: 3

mob
mob

Reputation: 118595

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 ...

Upvotes: 2

Related Questions