Delphi - Обновление глобальной строки из второго потока

Я экспериментирую с многопоточностью в Delphi (XE) и столкнулся с проблемой использования глобальной переменной между основным потоком VCL и вторым рабочим потоком.

Мой проект включает в себя второй рабочий поток, который просматривает некоторые файлы и обновляет строку globalvar с включенным текущим именем файла. Этот globalvar затем выбирается с помощью таймера в главном потоке VCL и обновляет строку состояния.

Однако я заметил, что иногда возникает «недопустимая операция указателя» ... или «недостаточно памяти», или рабочий поток просто перестает отвечать (вероятно, тупик).

Поэтому я создал тестовое приложение, чтобы идентифицировать и значительно увеличить вероятность ошибки, чтобы я мог видеть, что происходит.

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);

Я установил интервал таймера на 10 мс, чтобы он много читал, пока рабочий поток не работает, обновляя глобальную строку var. Конечно, это приложение едва длится секунду при запуске, прежде чем оно появляется с вышеуказанными ошибками.

Мой вопрос: нужно ли запускать операцию чтения Global var в VCL Timer в критическом разделе? - если так, то почему? Насколько я понимаю, это только чтение, и с записями, уже запущенными в критическом разделе, я не могу понять, почему он сталкивается с проблемой. Если я помещаю чтение в таймере также в критическую секцию - это работает нормально .... но я недоволен, просто делая это, не зная, почему!

Я новичок в многопоточности, поэтому был бы признателен за любую помощь в объяснении, почему этот простой пример вызывает все виды проблем и если есть лучший способ получить доступ к строке из рабочего потока.

multithreading,delphi,delphi-xe,

9

Ответов: 1


11 принят

Delphi String размещается в куче, где-то это не статический буфер. Сама переменная является просто указателем. Когда ваш поток чтения получает доступ к строке, и в то же время эта строка освобождается другим потоком, происходят плохие вещи . Вы обращаетесь к уже освобожденной памяти, возможно, снова выделены для чего-то еще и т. Д.

Даже если эта строка является статическим буфером, операции обновления не являются атомарными, поэтому вы можете использовать поврежденную строку, которая обновляется в данный момент (половина новых данных и половина старых).

Поэтому вам нужно защитить свои операции чтения тем же критическим разделом, который вы использовали для операций записи.

многопоточность, Дельфи, Дельфи-х,
Похожие вопросы