TThread的Thread Safe字段本身是否线程安全?

4
在这段代码中:
TMyClass = class(TThread)
public
  FInputBuffer     : TThreadedQueue<TBytes>;
protected
  procedure Execute; override;
end;

在TMyClass和其他类中使用FInputBuffer线程安全吗?

编辑:

示例用法:在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;

在其他类中:

var MyClass :TMyClass ;

procedure TForm1.btn1Click(Sender: TObject);
var x :TBytes;
begin
  //set x
  MyClass.FInputBuffer.PushItem(x);
end;

  1. 你必须明确定义你所说的“线程安全”。对于这个术语有很多不同的解释方式。
  2. 你没有展示你的其他代码,所以即使我们知道你所指的“线程安全”是什么意思,我们也很难判断其他代码是否符合这个要求。
- David Heffernan
1
我的一个准则是:公共字段永远不是线程安全的,因为它们具有公共写入访问权限。 - Sir Rufo
1个回答

11
如果在线程开始运行之前在线程构造函数中创建了FInputBuffer,并且在线程析构函数中在线程完成运行后释放了它,则只要TMyClass对象仍然存在,从其他线程访问它是线程安全的,因为TThreadedQueue提供了其内部内容的线程安全性。展示的是多线程队列的完全有效使用,只要在调用btn1Click()MyClass变量是有效的。
但是,如果在Execute()内部创建FInputBuffer,那么它不是线程安全的,因为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;

如果您想在Execute()内创建FInputBuffer,则线程应公开一个标志/信号,在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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接