As the worker thread could still be adding items to the vector (possibly resulting in a resize/move) I'm thinking I can only do this while the worker thread is suspended
That is a very good idea.
but TThread.Suspend() is deprecated.
Even when it was not deprecated, it was still dangerous to use. Only a debugger should ever suspend a thread, that is what the SuspendThread()
API is intended for.
I've spent days looking at TMutex, TSemaphore etc. but the documentation is dire. Could anyone point me in the right direction?
You could simply wrap all access to the vector with a TCriticalSection
or TMutex
, then the main and worker threads can both enter the lock whenever they need to do anything with the vector. For example:
type
TMyThread = class(TThread)
private
FLock: TCriticalSection;
protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure Lock;
procedure Unlock;
end;
constructor TMyThread.Create;
begin
inherited Create(False);
FLock := TCriticalSection.Create;
end;
destructor TMyThread.Destroy;
begin
FLock.Free;
end;
procedure TMyThread.Lock;
begin
FLock.Enter;
end;
procedure TMyThread.Unlock;
begin
FLock.Leave;
end;
procedure TMyThread.Execute;
begin
while not Terminated do
begin
Lock;
try
// do something...
finally
Unlock;
end;
end;
end;
MyThread.Lock;
try
if Vector.Size >= X then
begin
// do something ...
end;
finally
MyThread.Unlock;
end;
If you find that the worker thread accesses the vector more than the main thread does, you might consider using a TMultiReadExclusiveWriteSynchronizer
or a SRW lock instead.
Or, you could use some TEvent
objects to signal the worker thread when to pause and when to resume. The main thread could then signal the thread to pause and wait for it to actually pause, then access the vector and unpause the thread when done. For example:
type
TMyThread = class(TThread)
private
FPauseEvent: TEvent;
FPausedEvent: TEvent;
FResumeEvent: TEvent;
procedure CheckForPause;
protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure Pause;
procedure Unpause;
end;
constructor TMyThread.Create;
begin
inherited Create(False);
FPauseEvent := TEvent.Create(nil, True, False, '');
FPausedEvent := TEvent.Create(nil, True, False, '');
FResumeEvent := TEvent.Create(nil, True, True, '');
end;
destructor TMyThread.Destroy;
begin
FPauseEvent.Free;
FPausedEvent.Free;
FResumeEvent.Free;
end;
procedure TMyThread.Pause;
begin
FResumeEvent.ResetEvent;
FPauseEvent.SetEvent;
FPausedEvent.WaitFor(Infinite);
end;
procedure TMyThread.Unpause;
begin
FPauseEvent.ResetEvent;
FResumeEvent.SetEvent;
end;
procedure TMyThread.CheckForPause;
begin
if FPauseEvent.WaitFor(0) = wrSignaled then
begin
FPausedEvent.SetEvent;
FResumeEvent.WaitFor(Infinite);
FPausedEvent.ResetEvent;
end;
end;
procedure TMyThread.Execute;
begin
while not Terminated do
begin
CheckForPause;
if Terminated then Exit;
// do something...
end;
end;
MyThread.Pause;
try
if Vector.Size >= X then
begin
// do something ...
end;
finally
MyThread.Unpause;
end;