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