开发者

Is it safe to access VT data from the other thread?

开发者 https://www.devze.com 2023-02-22 16:43 出处:网络
Is it safe to change VirtualTreeView data from the secondary thread ? And if yes, should I use critical sections (or even Synchronize method) ?

Is it safe to change VirtualTreeView data from the secondary thread ? And if yes, should I use critical sections (or even Synchronize method) ?

I'm afraid that when I'll be writing to the VT's data record from the other thread, main thread invokes its repaint meanwhile and this refresh will cause reading of the same record at one time. I would supplement I'm using only 2 threads in the application.

Something like ...

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;

Update

I've added the critical sections to the code, is it really unsafe to call GetNodeDa开发者_运维知识库ta from TMyCreatedThread class, even if this function only returns a pointer to the record ?

Thanks a lot

Regards


No, especially the way you're doing it.

VST is a visual control. So is MainForm, which you're referring to directly from your thread. GUI controls are not thread-safe, and shouldn't be accessed directly from a thread. In addition, you're referring to the global variable 'MainForm' from the thread. This is absolutely NOT thread-safe.

If you need to access the data for the VST from both your main form and a separate thread, don't store it directly in VST.Node.Data. Keep it in an external list that you can surround with a critical section or some other thread-safe method, and access that external list in the thread (locking it first) or your main form in the VST events. See TLockList in the Delphi RTL for an example of a lockable list, and TMultipleReadExclusiveWrite for a sample synchronization class.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号