Multithreading 在TVirtualStringTree中显示文件系统树数据

Multithreading 在TVirtualStringTree中显示文件系统树数据,multithreading,delphi,virtualtreeview,tvirtualstringtree,Multithreading,Delphi,Virtualtreeview,Tvirtualstringtree,对于文件系统对象,我有这样的线程安全类: type PFSObject = ^TFSObject; TFSObject = class private FMREW: TMREWSync; FChildren: TObjectList<TFSObject>; FFilesCount: UInt32; FFoldersCount: UInt32; FName: string; FParent: TFSObject;

对于文件系统对象,我有这样的线程安全类:

type
  PFSObject = ^TFSObject;
  TFSObject = class
  private
    FMREW: TMREWSync;

    FChildren: TObjectList<TFSObject>;
    FFilesCount: UInt32;
    FFoldersCount: UInt32;
    FName: string;
    FParent: TFSObject;

    function GetFullPath: string;
  public
    constructor Create(const AName: string; AParent: TFSObject; AFilesCount, AFoldersCount: UInt32 = 0);
    destructor Destroy; override;

    property Children: TObjectList<TFSObject> read FChildren write FChildren;
    property FilesCount: UInt32 read FFilesCount write FFilesCount;
    property FoldersCount: UInt32 read FFoldersCount write FFoldersCount;
    property Name: string read FName write FName;
    property Parent: TFSObject read FParent write FParent;

    procedure LockRead;
    procedure LockWrite;
    procedure UnlockRead;
    procedure UnlockWrite;
  end;
类型
PFSObject=^TFSObject;
TFSObject=类
私有的
FMREW:TMREWSync;
fcchildren:TObjectList;
ffilesscount:UInt32;
FFoldersCount:UInt32;
FName:字符串;
FParent:TFSObject;
函数GetFullPath:string;
公众的
构造函数创建(const AName:string;AParent:TFSObject;afilescont,afolderscont:UInt32=0);
毁灭者毁灭;推翻
属性子项:TObjectList读取FChildren写入FChildren;
属性FileCount:UInt32读取FFilesCount写入FFilesCount;
属性FoldersCount:UInt32读FFoldersCount写FFoldersCount;
属性名称:字符串读取FName写入FName;
属性父级:TFSObject读取FParent写入FParent;
程序锁读;
程序锁写;
程序解锁;
程序解锁写入;
结束;
有线程,它扫描文件系统并填充这个。 在主窗体上有计时器,它从这个类接收数据以显示在TVirtualStringTree中

在TVirtualStringTree中显示此类数据的最佳方法是什么,而不需要释放额外的内存来将数据副本存储在节点中

更新: 好的,我现在有了

type
  PSizeData = ^TSizeData;
  TSizeData = record
    FSObj: PFSObject;
  end;

// OnTimer reader
procedure TformSize.tmrSizeTimer(Sender: TObject);
begin
  if tvSize.RootNodeCount = 0 then
    tvSize.RootNodeCount := 1
  else begin
    tvSize.Repaint;
    if FSThread.Finished then begin
      // Thread finished, disable timer
      SetTimerEnabled(False);
      // Expant first node
      tvSize.Expanded[tvSize.GetFirst] := True;
    end;
  end;
end;

// GetText of TVirtualStringTree
procedure TformSize.tvSizeGetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType; var CellText: string);
var
  Data, ParData: PSizeData;
begin
  // Check that children count changed for node
  Data := tvSize.GetNodeData(Node);
  if (Int32(Node.ChildCount) <> Data.FSObj.Children.Count) then begin
    tvSize.ChildCount[Node] := Data.FSObj.Children.Count;
    // Check that children count changed for parent node
    ParData := tvSize.GetNodeData(Node.Parent); 
    if Assigned(ParData) and (Int32(Node.Parent.ChildCount) <> ParData.FSObj.Children.Count) then
      tvSize.ChildCount[Node.Parent] := ParData.FSObj.Children.Count;
  end;
  // Get node text
  CellText := GetSizeDataText(Data, Column);
end;

// InitNode of TVirtualStringTree
procedure TformSize.tvSizeInitNode(Sender: TBaseVirtualTree; ParentNode, Node: PVirtualNode;
  var InitialStates: TVirtualNodeInitStates);
var
  Data, ParData: PSizeData;
  PFSObj: PFSObject;
begin
  Data  := Sender.GetNodeData(Node);
  if not Assigned(ParentNode) then
    PFSObj := @FSThread.FSObject
  else begin
    ParData := Sender.GetNodeData(ParentNode);
    PFSObj := PFSObject(ParData.FSObj.Children[Node.Index]);
  end;
  Data.FSObj := PFSObj;
end;
类型
PSizeData=^TSizeData;
TSizeData=记录
FSObj:PFSObject;
结束;
//实时阅读器
程序TformSize.tmrSizeTimer(发送方:TObject);
开始
如果tvSize.RootNodeCount=0,则
tvSize.RootNodeCount:=1
否则开始
tvSize.重新喷漆;
如果FSThread.Finished,则开始
//线程已完成,禁用计时器
SetTimerEnabled(假);
//出口第一节点
tvSize.Expanded[tvSize.GetFirst]:=True;
结束;
结束;
结束;
//TVirtualStringTree的GetText
过程TformSize.tvSizeGetText(发送方:TBaseVirtualTree;节点:PVirtualNode;列:TColumnIndex;
TextType:TVSTTextType;var CellText:string);
变量
数据,ParData:PSizeData;
开始
//检查节点的子节点数是否已更改
数据:=tvSize.GetNodeData(节点);
if(Int32(Node.ChildCount)Data.FSObj.Children.Count)然后开始
tvSize.ChildCount[节点]:=Data.FSObj.Children.Count;
//检查父节点的子节点数是否已更改
ParData:=tvSize.GetNodeData(Node.Parent);
如果分配了(ParData)和(Int32(Node.Parent.ChildCount)ParData.FSObj.Children.Count),那么
tvSize.ChildCount[Node.Parent]:=ParData.FSObj.Children.Count;
结束;
//获取节点文本
CellText:=GetSizeDateText(数据,列);
结束;
//TVirtualStringTree的InitNode
过程TformSize.tvSizeInitNode(发送方:TBaseVirtualTree;父节点,节点:PVirtualNode;
var初始状态:tVirtualNodeInitState);
变量
数据,ParData:PSizeData;
PFSObj:PFSObject;
开始
数据:=Sender.GetNodeData(节点);
如果未分配(父节点),则
PFSObj:=@FSThread.FSObject
否则开始
ParData:=Sender.GetNodeData(ParentNode);
PFSObj:=PFSObject(ParData.FSObj.Children[Node.Index]);
结束;
Data.FSObj:=PFSObj;
结束;
现在我在TVirtualStringTree中的内存不足:(
我的错误在哪里?

这肯定是VCL主线程和文件读/写线程之间的线程问题

使用VCL时(请记住:TVirtualStringTree是VCL组件),您需要将其他线程与VCL主线程同步

我过去为避免此类问题所做的工作:

  • 在VCL主线程中创建一个互斥体(又名TForm或其他)
  • 创建线程并将互斥对象传递给它
  • 运行线程,访问VCL属性时,在之前执行互斥锁,在之后执行互斥解锁
  • 在VCL主线程中也执行互斥锁/解锁

  • 基本上,在没有安全同步的情况下,您不应该从其他线程访问或更改VCL属性。

    这肯定是VCL主线程和文件读/写线程之间的线程问题

    使用VCL时(请记住:TVirtualStringTree是VCL组件),您需要将其他线程与VCL主线程同步

    我过去为避免此类问题所做的工作:

  • 在VCL主线程中创建一个互斥体(又名TForm或其他)
  • 创建线程并将互斥对象传递给它
  • 运行线程,访问VCL属性时,在之前执行互斥锁,在之后执行互斥解锁
  • 在VCL主线程中也执行互斥锁/解锁

  • 基本上,在没有安全同步的情况下,您不应该从其他线程访问或更改VCL属性。

    您说的是“线程安全”但没有证据表明这一点。这是课程的一小部分,没有阅读和写作的关键部分。当然,上面的课程不是线程安全的,任何人都很难在没有任何线程安全细节的情况下就如何在多线程场景中使用类型向您提供建议。这些方面很可能相互作用。预期的se会影响线程安全设计。很可能您当前的线程安全实现不适合您的应用程序。将我的读写实现添加到类中。类的所有字段都在成对的LockRead/UnlockRead、LockWrite/UnlockWrite中使用。您可以有多个读卡器,但只有一个写卡器?如果有作家,没有读者?啊,是的,我看到了
    TMREWSync
    。你说的是“线程安全”但没有证据表明这一点。这是课程的一小部分,没有阅读和写作的关键部分。当然,上面的课程不是线程安全的,任何人都很难在没有任何线程安全细节的情况下就如何在多线程场景中使用类型向您提供建议。这些方面很可能相互作用。预期的se影响线程安全设计。很可能您当前的线程安全实现不适合您的应用程序。已将我的读写实现添加到类中。类的所有字段都在成对的LockRead/UnlockRead、LockWrite/UnlockWrite中使用