Waitgroups in Pascal
Our first program will demonstrate the use of WaitGroups to synchronize multiple threads. Here’s the full source code:
program WaitGroups;
{$mode objfpc}{$H+}
uses
SysUtils, Classes;
type
TWorker = class(TThread)
private
FId: Integer;
public
constructor Create(AId: Integer);
procedure Execute; override;
end;
TWaitGroup = class
private
FCounter: Integer;
FLock: TCriticalSection;
FEvent: TEvent;
public
constructor Create;
destructor Destroy; override;
procedure Add(Delta: Integer);
procedure Done;
procedure Wait;
end;
var
WaitGroup: TWaitGroup;
constructor TWorker.Create(AId: Integer);
begin
inherited Create(False);
FreeOnTerminate := True;
FId := AId;
end;
procedure TWorker.Execute;
begin
WriteLn(Format('Worker %d starting', [FId]));
Sleep(1000); // Simulate an expensive task
WriteLn(Format('Worker %d done', [FId]));
WaitGroup.Done;
end;
constructor TWaitGroup.Create;
begin
inherited;
FCounter := 0;
FLock := TCriticalSection.Create;
FEvent := TEvent.Create(nil, True, False, '');
end;
destructor TWaitGroup.Destroy;
begin
FLock.Free;
FEvent.Free;
inherited;
end;
procedure TWaitGroup.Add(Delta: Integer);
begin
FLock.Enter;
try
Inc(FCounter, Delta);
if FCounter > 0 then
FEvent.ResetEvent;
finally
FLock.Leave;
end;
end;
procedure TWaitGroup.Done;
begin
FLock.Enter;
try
Dec(FCounter);
if FCounter = 0 then
FEvent.SetEvent;
finally
FLock.Leave;
end;
end;
procedure TWaitGroup.Wait;
begin
FEvent.WaitFor(INFINITE);
end;
var
I: Integer;
begin
WaitGroup := TWaitGroup.Create;
try
for I := 1 to 5 do
begin
WaitGroup.Add(1);
TWorker.Create(I);
end;
WaitGroup.Wait;
finally
WaitGroup.Free;
end;
end.
This program demonstrates the use of a WaitGroup to synchronize multiple threads in Pascal. Here’s a breakdown of the code:
We define a
TWorker
class that extendsTThread
. Each worker prints a message when starting and finishing, and simulates work with aSleep
call.We implement a
TWaitGroup
class that mimics the functionality of Go’s WaitGroup. It uses a counter, a critical section for thread-safe operations, and an event for synchronization.In the main program, we create a
TWaitGroup
instance and launch 5 worker threads.Each worker thread is created with
WaitGroup.Add(1)
to increment the counter.The
WaitGroup.Wait
method is called to block until all workers have finished.Each worker calls
WaitGroup.Done
when it completes its work.
To run this program, save it as waitgroups.pas
and compile it with the Free Pascal Compiler:
$ fpc waitgroups.pas
$ ./waitgroups
The output will be similar to:
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
Note that the order of workers starting and finishing may vary between runs due to the nature of concurrent execution.
This approach provides a way to wait for multiple threads to finish their work. However, it doesn’t provide a straightforward way to propagate errors from workers. For more advanced use cases, you might need to implement additional error handling mechanisms.