Delphi - 从第二个线程更新全局字符串

fro*_*deg 9 delphi multithreading delphi-xe

我正在Delphi(XE)中尝试多线程,并且遇到了在主VCL线程和第二个工作线程之间使用全局变量的问题.

我的项目涉及第二个工作线程,它扫描一些文件,并使用当前文件名更新globalvar字符串.然后通过主VCL线程上的计时器拾取此globalvar,并更新状态栏.

我注意到它偶尔会出现'无效的指针操作'......或'内存不足'或工作线程只是停止响应(可能是死锁).

因此,我创建了一个测试应用程序来识别并大大增加出错的可能性,以便我可以看到正在发生的事情.

type
  TSyncThread = class(TThread)
  protected
    procedure Execute; override;
end;

var
  Form11: TForm11;
  ProgressString : String;
  ProgressCount : Int64;
  SyncThread : TSyncThread;
  CritSect : TRTLCriticalSection;

implementation

{$R *.dfm}

procedure TForm11.StartButtonClick(Sender: TObject);
begin
  Timer1.Enabled := true;
  SyncThread := TSyncThread.Create(True);
  SyncThread.Start;
end;

procedure TForm11.StopbuttonClick(Sender: TObject);
begin
  Timer1.Enabled := false;
  SyncThread.Terminate;
end;

procedure TForm11.Timer1Timer(Sender: TObject);
begin
  StatusBar1.Panels[0].Text := 'Count: ' + IntToStr(ProgressCount);
  StatusBar1.Panels[1].Text := ProgressString;
end;

procedure TSyncThread.Execute;
var
  i : Int64;
begin
  i := 0;
  while not Terminated do begin
    inc(i);
    EnterCriticalSection(CritSect);
    ProgressString := IntToStr(i);
    ProgressCount := i;
    LeaveCriticalSection(CritSect);
  end;
end;

initialization
  InitializeCriticalSection(CritSect);
finalization
  DeleteCriticalSection(CritSect);
Run Code Online (Sandbox Code Playgroud)

我将计时器间隔设置为10毫秒,以便它读取很多,而工作线程正在运行,更新全局变量字符串.这个应用程序在运行之前几乎不会持续一秒,然后才会出现上述错误.

我的问题是,VCL Timer中Global var的读操作是否需要在关键部分运行? - 如果是这样,为什么?根据我的理解,它只是一个读取,并且已经在关键部分中运行写入,我无法理解为什么它会遇到问题.如果我确实把计时器中的读数放入一个关键部分 - 它工作正常....但我不高兴只是这样做而不知道为什么!

我是多线程新手所以非常感谢任何帮助,解释为什么这个简单的例子会导致各种各样的问题,以及是否有更好的方法从工作线程访问字符串.

hai*_*img 11

Delphi String是在堆上分配的,它不是某个地方的静态缓冲区.变量本身只是一个指针.当你的阅读线程访问一个字符串,同时这个字符串被另一个线程释放时,会发生不好的事情.您正在访问已释放的内存,可能会再次分配其他内容等.

即使此String是静态缓冲区,更新操作也不是原子的,因此您可能正在使用此时正在更新的损坏的字符串(一半新数据和一半旧数据).

因此,您需要使用在编写操作周围使用的相同关键部分来保护您的阅读操作.