Mutexes in Perl

In the previous example we saw how to manage simple counter state using atomic operations. For more complex state we can use a mutex to safely access data across multiple threads.

use strict;
use warnings;
use threads;
use Thread::Semaphore;
use Data::Dumper;

# Container holds a hash of counters; since we want to
# update it concurrently from multiple threads, we
# add a semaphore to synchronize access.
package Container {
    sub new {
        my $class = shift;
        my $self = {
            semaphore => Thread::Semaphore->new(),
            counters => { a => 0, b => 0 },
        };
        return bless $self, $class;
    }

    # Lock the semaphore before accessing counters; unlock
    # it at the end of the function.
    sub inc {
        my ($self, $name) = @_;
        $self->{semaphore}->down();
        $self->{counters}->{$name}++;
        $self->{semaphore}->up();
    }
}

# This function increments a named counter
# in a loop.
sub do_increment {
    my ($container, $name, $n) = @_;
    for (1..$n) {
        $container->inc($name);
    }
}

my $c = Container->new();

# Run several threads concurrently; note
# that they all access the same Container,
# and two of them access the same counter.
my @threads = (
    threads->create(\&do_increment, $c, 'a', 10000),
    threads->create(\&do_increment, $c, 'a', 10000),
    threads->create(\&do_increment, $c, 'b', 10000),
);

# Wait for the threads to finish
$_->join for @threads;

print Dumper($c->{counters});

Running the program shows that the counters updated as expected.

$ perl mutexes.pl
$VAR1 = {
          'a' => 20000,
          'b' => 10000
        };

Next we’ll look at implementing this same state management task using only threads and queues.

In this Perl implementation:

  1. We use the threads module for creating and managing threads, which is similar to goroutines in Go.

  2. Instead of a mutex, we use a Thread::Semaphore for synchronization. This serves the same purpose as the mutex in the Go example.

  3. The Container is implemented as a Perl package with methods for initialization and incrementing counters.

  4. The inc method uses the semaphore’s down and up methods to lock and unlock access to the shared data, similar to Lock and Unlock in Go.

  5. We create threads using threads->create and join them using the join method, which is analogous to using goroutines and WaitGroup in Go.

  6. Finally, we use Data::Dumper to print the contents of the counters hash, which gives a similar output to the Go version.

This Perl code demonstrates the same concepts of safe concurrent access to shared data as the original Go example, adapted to Perl’s threading and synchronization mechanisms.