在 TVirtualStringTree 中显示文件系统树数据
Show file system tree data in TVirtualStringTree
我有这样的文件系统对象线程安全class:
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;
有扫描文件系统并填充它的线程。
在主窗体上有计时器,它从这个 class 接收数据以显示在 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;
现在我的 TVirtualStringTree 内存不足:(
我的错误在哪里?
这肯定是 VCL 主线程和文件读/写线程之间的线程问题。
使用 VCL 时(记住:TVirtualStringTree 是一个 VCL 组件)您需要将其他线程与 VCL 主线程同步。
我过去为避免此类问题所做的工作:
- 在 VCL 主线程(又名您的 TForm 或其他东西)中创建一个互斥量
- 创建线程并将互斥锁传递给它
- 运行 线程和访问 VCL 属性时在之前执行互斥锁并在之后执行互斥锁解锁
- 在 VCL 主线程中也执行互斥锁/解锁
基本上,在没有安全同步的情况下,您不应该从其他线程访问或更改 VCL 属性。
我有这样的文件系统对象线程安全class:
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;
有扫描文件系统并填充它的线程。 在主窗体上有计时器,它从这个 class 接收数据以显示在 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;
现在我的 TVirtualStringTree 内存不足:( 我的错误在哪里?
这肯定是 VCL 主线程和文件读/写线程之间的线程问题。
使用 VCL 时(记住:TVirtualStringTree 是一个 VCL 组件)您需要将其他线程与 VCL 主线程同步。
我过去为避免此类问题所做的工作:
- 在 VCL 主线程(又名您的 TForm 或其他东西)中创建一个互斥量
- 创建线程并将互斥锁传递给它
- 运行 线程和访问 VCL 属性时在之前执行互斥锁并在之后执行互斥锁解锁
- 在 VCL 主线程中也执行互斥锁/解锁
基本上,在没有安全同步的情况下,您不应该从其他线程访问或更改 VCL 属性。