Stateful Goroutines in Perl

In this example, we’ll demonstrate how to manage state using threads and shared data structures in Perl. This approach differs from Go’s goroutines and channels, but it achieves a similar result of concurrent access to shared state.

use strict;
use warnings;
use threads;
use threads::shared;
use Time::HiRes qw(sleep);
use Scalar::Util qw(looks_like_number);

# Define shared data structures
my %state : shared;
my $read_ops : shared = 0;
my $write_ops : shared = 0;

# Function to perform read operations
sub read_op {
    while (1) {
        my $key = int(rand(5));
        {
            lock(%state);
            my $value = $state{$key} // 0;
            $read_ops++;
        }
        sleep(0.001);  # Sleep for 1 millisecond
    }
}

# Function to perform write operations
sub write_op {
    while (1) {
        my $key = int(rand(5));
        my $val = int(rand(100));
        {
            lock(%state);
            $state{$key} = $val;
            $write_ops++;
        }
        sleep(0.001);  # Sleep for 1 millisecond
    }
}

# Create threads for read operations
my @read_threads;
for my $i (1..100) {
    push @read_threads, threads->create(\&read_op);
}

# Create threads for write operations
my @write_threads;
for my $i (1..10) {
    push @write_threads, threads->create(\&write_op);
}

# Let the threads run for a second
sleep(1);

# Print the final operation counts
print "readOps: $read_ops\n";
print "writeOps: $write_ops\n";

# Detach all threads (they will continue running in the background)
$_->detach for (@read_threads, @write_threads);

This Perl script demonstrates concurrent access to shared state using threads. Here’s a breakdown of what’s happening:

  1. We use the threads and threads::shared modules to create threads and share data between them.

  2. We define shared variables: a hash %state to store our data, and two counters $read_ops and $write_ops to keep track of the number of operations.

  3. The read_op function simulates read operations. It randomly selects a key, reads the value from the shared state, and increments the read counter.

  4. The write_op function simulates write operations. It randomly selects a key and value, writes to the shared state, and increments the write counter.

  5. We create 100 threads for read operations and 10 threads for write operations.

  6. The main thread sleeps for a second to allow the other threads to run.

  7. Finally, we print the total number of read and write operations performed.

  8. We detach all threads, allowing them to continue running in the background even after the main thread exits.

This approach uses Perl’s threading model and shared variables to achieve concurrent access to shared state. The lock function is used to ensure thread-safe access to the shared hash.

Running this program will show the number of read and write operations completed in one second:

$ perl stateful_threads.pl
readOps: 83721
writeOps: 8372

The exact numbers may vary due to the nature of concurrent execution, but you should see a significant number of operations completed in just one second.

While this Perl implementation differs from the Go version in its use of threads instead of goroutines, it demonstrates a similar concept of managing shared state in a concurrent environment. Choose the approach that best fits your specific needs and the language you’re working with.