如果在线程开始运行之前在线程构造函数FInputBuffer
中创建,并在线程完成运行后在线程析构函数中释放,那么是的,当对象还活着时,从其他线程访问它是线程安全的,因为提供了它的其内部内容拥有自己的线程安全性。您所展示的是对多线程队列的完全有效使用,前提是该变量在调用时有效。TMyClass
TThreadedQueue
MyClass
btn1Click()
但是,如果在FInputBuffer
内部创建Execute()
,则它不是线程安全的,因为btn1Click()
可能会在线程开始运行之前尝试访问队列,在FInputBuffer
创建之前。这就是为什么您需要FInputBuffer
在构造函数中创建,例如:
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;
如果要在FInputBuffer
内部创建Execute()
,则线程应该公开一个在实际创建后设置的标志/信号,FInputBuffer
然后在设置该标志/信号之前,任何其他代码都不应尝试访问FInputBuffer
。创建线程实例的代码应等待该标志/信号,然后再将控制权返回给其余代码,例如:
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;