Waitgroups in Perl

In Perl, we can use threads and a shared variable to achieve similar functionality to WaitGroups. Here’s how we can implement the same concept:

use strict;
use warnings;
use threads;
use Thread::Semaphore;
use Time::HiRes qw(sleep);

# This is the function we'll run in every thread
sub worker {
    my $id = shift;
    printf("Worker %d starting\n", $id);

    # Sleep to simulate an expensive task
    sleep(1);
    printf("Worker %d done\n", $id);
}

# Create a semaphore to act as our WaitGroup
my $wg = Thread::Semaphore->new(0);

# Launch several threads
for my $i (1..5) {
    threads->create(sub {
        worker($i);
        $wg->up(); # Signal that the thread is done
    });
}

# Wait for all threads to finish
$wg->down(5);

# Clean up threads
$_->join for threads->list;

To wait for multiple threads to finish, we can use a semaphore as a wait group.

We start by importing the necessary modules. threads allows us to create and manage threads, Thread::Semaphore provides semaphore functionality, and Time::HiRes allows for sub-second sleep times.

The worker function is defined to simulate some work being done. It prints a message when starting and finishing, with a 1-second sleep in between to simulate an expensive task.

In the main part of the script:

  1. We create a semaphore initialized to 0. This will act as our wait group.

  2. We launch 5 threads using a loop. Each thread runs a closure that calls the worker function and then signals completion by calling $wg->up().

  3. After launching all threads, we call $wg->down(5). This will block until the semaphore has been incremented 5 times, effectively waiting for all threads to complete.

  4. Finally, we join all threads to clean up resources.

To run the program, save it as waitgroups.pl and use:

$ perl waitgroups.pl
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 4 starting
Worker 5 starting
Worker 1 done
Worker 2 done
Worker 3 done
Worker 4 done
Worker 5 done

The order of workers starting up and finishing is likely to be different for each invocation.

Note that this approach doesn’t provide a straightforward way to propagate errors from threads. For more advanced use cases, you might need to implement additional error handling mechanisms.