我必须修改和更改线程中的一些可视组件,因为你知道这样做是不安全的。
我的问题是如何编写完全线程安全的代码?有可能吗?如果是的话那么请给我一个简单的例子吗?
我的代码不是线程安全的:
type
tMyWorkerThread = class(TThread)
public
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure tMyWorkerThread.Execute;
begin
//codes
//working with visual components
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyWorkerThread.Create(false);
end;
谢谢。
答案 0 :(得分:9)
在Delphi中编写线程安全代码涉及到任何其他语言的基本护理,这意味着要处理竞争条件。当不同的线程访问相同的数据时会发生竞争条件。解决这个问题的一个好方法是声明 TCriticalSection 的实例并将危险的代码包装在其中。
下面的代码显示了一个getter和一个属性的setter,通过hypotesis,它具有竞争条件。
constructor TMyThread.Create;
begin
CriticalX := TCriticalSection.Create;
end;
destructor TMyThread.Destroy; override;
begin
FreeAndNil(CriticalX);
end;
function TMyThread.GetX: string;
begin
CriticalX.Enter;
try
Result := FX;
finally
CriticalX.Leave;
end;
end;
procedure TMyThread.SetX(const value: string);
begin
CriticalX.Enter;
try
FX := Value;
finally
CriticalX.Leave;
end;
end;
请注意使用 TCriticalSection ( CriticalX )的单个实例来序列化对数据成员 FX 的访问。
然而,对于Delphi,您有一个额外的考虑因素! VCL不是线程安全的,因此为了避免VCL竞争条件,导致屏幕更改的任何操作都必须在主线程中运行。您可以通过在 Synchronize 方法中调用此类代码来实现此目的。考虑到上面的类,你应该做这样的事情:
procedure TMyThread.ShowX;
begin
Synchronize(SyncShowX);
end;
procedure TMyThread.SyncShowX;
begin
ShowMessage(IntToStr(FX));
end;
如果你有 Delphi 2010 或更高版本,有一种更简单的方法可以使用匿名方法:
procedure TMyThread.ShowX;
begin
Synchronize(procedure begin
ShowMessage(IntToStr(FX));
end);
end;
我希望这有帮助!
答案 1 :(得分:5)
您应该只从主VCL线程访问VCL对象。
某些读取方法(属性获取器)在实践中可以从其他线程开始工作 - 但是您必须事先为特定的Delphi构建读取VCL源代码。或者不使用它。
PS:Synchronize方法在主VCL线程中运行给定的程序,暂停调用程序线程,如果主线程也被阻塞,可能会导致死锁。
阅读更多:(实际上这个答案列出了一些链接)
答案 2 :(得分:0)
Synchronize!
type
tMyWorkerThread = class(TThread)
public
procedure Execute; override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure tMyWorkerThread.Execute;
begin
//codes that takes long time
Synchronize(procedure begin
//working with visual components
end
);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
TMyWorkerThread.Create(false);
end;
谢谢大家的帮助。