If a FInputBuffer created in the thread constructor before the thread starts working and is freed up in the thread destructor after the thread finishes, then yes, access to it from other threads is thread safe while the TMyClass object is still alive, because TThreadedQueue provides its own flow safety for its internal content. What you showed is a perfectly acceptable use of a multi-threaded queue if the MyClass variable is valid during the call to btn1Click() .
However, if a FInputBuffer is created inside Execute() , then it is not thread safe, because btn1Click() may try to access the queue before the thread starts working before the FInputBuffer created. This is why you need to create a FInputBuffer in the constructor, for example:
TMyClass = class(TThread) public FInputBuffer: TThreadedQueue<TBytes>; constructor Create(ACreateSuspended: Boolean); override; destructor Destroy; override; protected procedure Execute; override; end; constructor TMyClass.Create(ACreateSuspended: Boolean); begin inherited; FInputBuffer := TThreadedQueue<TBytes>.Create; end; destructor TMyClass.Destroy; begin FInputBuffer.Free; inherited; end; procedure TMyClass.Execute; var x: TBytes; begin while not Terminated do begin if FInputBuffer.QueueSize > 0 then begin x := FInputBuffer.PopItem; // some code to use x end; end; end;
If you want to create a FInputBuffer inside Execute() , then the stream must set the flag / signal that is set after the FInputBuffer actually been created, and then no other code should try to access the FInputBuffer until the flag / signal set. The code that creates the thread instance must wait for this flag / signal before returning control back to the rest of the code, for example:
TMyClass = class(TThread) public FInputBuffer: TThreadedQueue<TBytes>; FInputBufferCreated: TEvent; constructor Create(ACreateSuspended: Boolean); override; destructor Destroy; override; protected procedure Execute; override; procedure DoTerminate; override; end; constructor TMyClass.Create(ACreateSuspended: Boolean); begin inherited; FInputBufferCreated := TEvent.Create(nil, True, False, ''); end; destructor TMyClass.Destroy; begin FInputBufferCreated.Free; inherited; end; procedure TMyClass.Execute; var x: TBytes; begin FInputBuffer := TThreadedQueue<TBytes>.Create; FInputBufferCreated.SetEvent; while not Terminated do begin if FInputBuffer.QueueSize > 0 then begin x := FInputBuffer.PopItem; // some code to use x end; end; end; procedure TMyClass.DoTerminate; begin if FInputBufferCreated <> nil then FInputBufferCreated.ResetEvent; FreeAndNil(FInputBuffer); inherited; end;
.
var MyClass: TMyClass = nil; procedure TForm1.StartBufferThread; var I: Integer; begin MyClass := TMyClass.Create(False); if MyClass.FInputBufferCreated.WaitFor(2500) <> wrSignaled then begin MyClass.Terminate; MyClass.WaitFor; FreeAndNil(MyClass); raise Exception.Create('MyClass.FInputBuffer not created after 2.5 seconds!'); end; end; procedure TForm1.btn1Click(Sender: TObject); var x: TBytes; begin //set x if MyClass <> nil then MyClass.FInputBuffer.PushItem(x); end;
source share