Безопасно ли получать доступ к данным VT из другого потока? - PullRequest
4 голосов
/ 07 апреля 2011

Безопасно ли изменять данные VirtualTreeView из вторичного потока?И если да, я должен использовать критические секции (или даже метод Synchronize)?

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

Что-то вроде ...

type
  PSomeRecord = ^TSomeRecord;
  TSomeRecord = record
    SomeString: string;
    SomeInteger: integer;
    SomeBoolean: boolean;
end;

...
var FCriticalSection: TRTLCriticalSection; // global for both classes
...

procedure TMyCreatedThread.WriteTheTreeData;
var CurrentData: PSomeRecord;
begin
  EnterCriticalSection(FCriticalSection); // I want to protect only the record

  CurrentData := MainForm.VST.GetNodeData(MainForm.VST.TopNode);

  with CurrentData^ do // I know, the ^ is not necessary but I like it :)
    begin
      SomeString := 'Is this safe ? What if VT will want this data too ?';
      SomeInteger := 777;
      SomeBoolean := True;
    end;

  LeaveCriticalSection(FCriticalSection);

  MainForm.VST.Invalidate;
end;

// at the same time in the main thread VT needs to get text from the same data
// is it safe to do it this way ?

procedure TMainForm.VST_GetText(Sender: TBaseVirtualTree;
  Node: PVirtualNode; Column: TColumnIndex; TextType: TVSTTextType;
  var CellText: string);
var CurrentData: PSomeRecord;
begin
  EnterCriticalSection(FCriticalSection); // I want to protect only the record

  CurrentData := VST.GetNodeData(VST.TopNode);

  with CurrentData^ do
    begin
      case Column of
        0: CellText := SomeString;
        1: CellText := IntToStr(SomeInteger);
        2: CellText := BoolToStr(SomeBoolean);
      end;
    end;

  LeaveCriticalSection(FCriticalSection);
end;

// I'm afraid the concurrent field reading may happen only here with the private VT fields
// FNodeDataSize, FRoot and FTotalInternalDataSize, since I have Node.Data locked by the
// critical sections in the VT events, some of those may be accessed when VT is refreshed
// somehow

function TBaseVirtualTree.GetNodeData(Node: PVirtualNode): Pointer;
begin
  Assert(FNodeDataSize > 0, 'NodeDataSize not initialized.');

  if (FNodeDataSize <= 0) or (Node = nil) or (Node = FRoot) then
    Result := nil
  else
    Result := PByte(@Node.Data) + FTotalInternalDataSize;
end;

Обновление

У меня естьдобавил критические разделы в код, действительно ли небезопасно вызывать GetNodeData из класса TMyCreatedThread, даже если эта функция возвращает только указатель на запись?

Большое спасибо

С уважением

1 Ответ

6 голосов
/ 07 апреля 2011

Нет, особенно то, как вы это делаете.

VST - это визуальный контроль.То же самое относится и к MainForm, на который вы ссылаетесь прямо из вашей ветки.Элементы управления графического интерфейса не являются поточно-ориентированными, и к ним нельзя обращаться напрямую из потока.Кроме того, вы ссылаетесь на глобальную переменную MainForm из потока.Это абсолютно НЕ потокобезопасно.

Если вам нужен доступ к данным для VST как из основной формы, так и из отдельного потока, не храните их непосредственно в VST.Node.Data.Сохраните его во внешнем списке, который вы можете окружить критическим разделом или каким-либо другим потокобезопасным методом, и получите доступ к этому внешнему списку в потоке (блокируя его первым) или к вашей основной форме в событиях VST.См. TLockList в Delphi RTL для примера блокируемого списка и TMultipleReadExclusiveWrite для примера класса синхронизации.

...