in this code :
TMyClass = class(TThread)
public
FInputBuffer : TThreadedQueue<TBytes>;
protected
procedure Execute; override;
end;
Does use (in TMyClass and in other class) of FInputBuffer is Thread Safe ?
EDIT:
sample use : in TMyClass :
procedure TMyClass.Execute;
var x :TBytes;
begin
inherited;
FInputBuffer:= TThreadedQueue<TBytes>.Create;
while not Terminated do begin
if FInputBuffer.QueueSize > 0 then begin
x:= FInputBuffer.PopItem;
//some code to use x
end;
end;
FInputBuffer.Free;
end;
In Other class :
var MyClass :TMyClass ;
procedure TForm1.btn1Click(Sender: TObject);
var x :TBytes;
begin
//set x
MyClass.FInputBuffer.PushItem(x);
end;
If FInputBuffer is created in the thread constructor before the thread begins running, and is freed in the thread destructor after the thread has finished running, then yes, access to it from other threads is thread-safe while the TMyClass object is still alive, because TThreadedQueue provides its own thread-safety for its inner content. What you have shown is a perfectly valid use of a multi-threaded queue, provided the MyClass variable is valid at the time btn1Click() is called.
However, if FInputBuffer is created inside of Execute(), then it is not thread-safe, because btn1Click() might try to access the queue before the thread has started running, before FInputBuffer has been created. That is why you need to create FInputBuffer in the constructor instead, eg:
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 FInputBuffer inside of Execute(), then thread should expose a flag/signal that gets set after FInputBuffer has actually been created, and then no other code should attempt to access FInputBuffer until that flag/signal has been set. The code that creates the thread instance should wait for that flag/signal before returning control back to the rest of the code, eg:
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With