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.