从其他线程访问VT数据是否安全?

4 delphi multithreading synchronization critical-section virtualtreeview

从辅助线程更改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;
Run Code Online (Sandbox Code Playgroud)

更新

我已经在代码中添加了关键部分,从TMyCreatedThread类调用GetNodeData是否真的不安全,即使此函数只返回指向记录的指针?

非常感谢

问候

Ken*_*ite 6

不,尤其是你这样做的方式.

VST是一种视觉控制.那么MainForm,你直接从你的线程中引用它.GUI控件不是线程安全的,不应直接从线程访问.另外,你指的是线程中的全局变量'MainForm'.这绝对不是线程安全的.

如果您需要访问VST主表单和单独线程的数据,请不要直接存储VST.Node.Data.将它保存在一个外部列表中,您可以使用关键部分或其他一些线程安全方法将其包围,并访问该线程中的外部列表(首先将其锁定)或您的主表单在VST事件中.有关TLockList可锁定列表TMultipleReadExclusiveWrite的示例以及示例同步类,请参阅Delphi RTL .

  • 读取这些字符串字段也是不安全的.如果两个线程可以同时访问同一个字符串,那么该字符串的引用计数必须至少为2.如果两个线程都通过相同的单个*变量*访问该字符串(在这种情况下它们是这样的,因为它只有任何节点的一个数据记录),然后引用计数将只有1.当任一线程访问该线程并且看到引用计数为1时,它将假定它具有对该字符串的独占访问权. (2认同)