Mutexes in Pascal

In this example, we’ll explore how to manage complex state using a mutex to safely access data across multiple threads in Pascal.

program Mutexes;

uses
  SysUtils, SyncObjs, Classes, Generics.Collections;

type
  TContainer = class
  private
    FMutex: TCriticalSection;
    FCounters: TDictionary<string, Integer>;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Inc(const Name: string);
    function GetCounters: TDictionary<string, Integer>;
  end;

constructor TContainer.Create;
begin
  inherited;
  FMutex := TCriticalSection.Create;
  FCounters := TDictionary<string, Integer>.Create;
  FCounters.Add('a', 0);
  FCounters.Add('b', 0);
end;

destructor TContainer.Destroy;
begin
  FMutex.Free;
  FCounters.Free;
  inherited;
end;

procedure TContainer.Inc(const Name: string);
begin
  FMutex.Enter;
  try
    FCounters[Name] := FCounters[Name] + 1;
  finally
    FMutex.Leave;
  end;
end;

function TContainer.GetCounters: TDictionary<string, Integer>;
begin
  Result := FCounters;
end;

procedure DoIncrement(Container: TContainer; const Name: string; N: Integer);
var
  I: Integer;
begin
  for I := 1 to N do
    Container.Inc(Name);
end;

var
  Container: TContainer;
  Thread1, Thread2, Thread3: TThread;

begin
  Container := TContainer.Create;
  try
    Thread1 := TThread.CreateAnonymousThread(procedure
    begin
      DoIncrement(Container, 'a', 10000);
    end);

    Thread2 := TThread.CreateAnonymousThread(procedure
    begin
      DoIncrement(Container, 'a', 10000);
    end);

    Thread3 := TThread.CreateAnonymousThread(procedure
    begin
      DoIncrement(Container, 'b', 10000);
    end);

    Thread1.Start;
    Thread2.Start;
    Thread3.Start;

    Thread1.WaitFor;
    Thread2.WaitFor;
    Thread3.WaitFor;

    for var Pair in Container.GetCounters do
      WriteLn(Pair.Key + ': ' + IntToStr(Pair.Value));
  finally
    Container.Free;
  end;
end.

In this Pascal example, we’ve created a TContainer class that holds a dictionary of counters. We use a TCriticalSection (which is Pascal’s equivalent of a mutex) to synchronize access to the counters.

The Inc method of TContainer is protected by the mutex, ensuring that only one thread can modify the counters at a time. We use a try..finally block to ensure that the mutex is always released, even if an exception occurs.

In the main program, we create three anonymous threads that each call the DoIncrement procedure. This procedure increments a named counter in a loop, similar to the goroutines in the original example.

After starting the threads, we wait for them to finish using WaitFor. Finally, we print out the values of the counters.

When you run this program, you should see output similar to this:

a: 20000
b: 10000

This demonstrates that the counters were incremented correctly, even with concurrent access from multiple threads.

Next, we could explore implementing this same state management task using only threads and message passing.