目录枚举第一次太慢

本文关键字:第一次 枚举 | 更新日期: 2023-09-27 18:21:13

我想尝试制作自己的文件资源管理器。我有一个算法来枚举所有驱动器中的所有目录。但它运行得太慢了。这是我的代码:

public ExplorerForm()
{
    InitializeComponent();
    this.SuspendLayout();//Without this, it will be far more slow again!
    string[] a = System.IO.Directory.GetLogicalDrives();// a is used for drive array
    for(int b = 0; b < a.Length; b++)//B is an enumerator. Ussually it is only one letter
    {
        //Defining the node
        TreeNode c = new TreeNode();//c i place for TreeNode
        c.Text = a[b].Substring(0,2);
        c.Tag = a[b];
        ApplyNodes(a[b], ref c);
        if(c != null) tv.Nodes.Add(a)
    }
    this.ResumeLayout(false);
}
private void ApplyNodes(string a, ref TreeNode b)//a=directory, b applied TreeNode
{
    try{
        List<string> c = new List<string>(Directory.EnumerateDirectories(a);//c = directories
        if (c.Count == 0 ) return;
        for(int d = 0; d < c.Count; d++)//d = enumerator.
        {
            TreeNode e = new TreeNode();//e = TreeNode
            var z = c[b].Split(Convert.ToChar("/"));
            e.Text = z[z.Length-1]
            e.Tag = c[b];
            ApplyNodes(c[b], e)
            if(e != null) b.Nodes.Add(e)
        }
    }catch (UnauthorizedAccessException){
    }catch (IOException){  //For test, It is removed. and my E: is not ready
    }
}

电视是我的控制。它运行得很慢。这表明当我删除选择行时,抛出IOException需要超过10秒的时间。帮助我如何改进枚举。这是除了使用线程和部分更新。如果以后无法修复,请告诉我原因。

目录枚举第一次太慢

除了之前关于改进TreeView填充调用的答案外,您还应该阅读MSDN页面"如何:枚举目录和文件"

第一段提到了一些性能改进(使用DirectoryInfo的可枚举集合而不是字符串)-注意最后一行:

您可以使用返回的方法枚举目录和文件其名称字符串的可枚举集合。您也可以使用返回DirectoryInfo的可枚举集合的方法,FileInfo或FileSystemInfo对象。可枚举集合提供当您使用大型的目录和文件。

然而,即使有了这种改进,您也应该而不是递归地降低ApplyNodes内部的整个子树。只需阅读一个级别,添加当前节点的条目,就可以大大减少需要遍历的子目录的数量(这肯定是文件资源管理器所做的。)这就是ta.speot.is 上面提到的"延迟加载"技术的要点

如果这两个改进仍然不能为您提供所需的性能,那么您可能需要增加更多的复杂性(例如,运行后台线程来执行遍历),但您需要首先清楚地知道代码的哪一部分是您的瓶颈(这意味着您需要添加定时代码和日志)

除了枚举系统上所有目录所需的时间(如果实现延迟加载,您可能会看到更好的性能)之外,将项插入TreeView可能会花费大量时间。

来自TreeView.BeginUpdate:

若要在向TreeView中一次添加一个项的同时保持性能,请调用BeginUpdate方法。BeginUpdate方法会阻止控件在调用EndUpdate方法之前进行绘制向树视图控件添加项的首选方法是使用AddRange方法向树视图添加树节点项的数组

若要允许控件恢复绘制,请在将所有树节点添加到树视图后调用EndUpdate方法。

虽然它与.NET不同,但Raymond Chen的博客文章《如何高效地将大量项目插入树视图》提供了更多信息,这些信息可能有助于您以一种能够提高项目插入性能的方式构建代码。

如果你需要在树视图中插入大量的项目,比如数万个,那么插入它们会更有效率;向后";。

编辑

下面是一个将目录枚举放到线程上的示例。观察TreeView控件的可用性(或缺乏可用性)。如果没有别的,这可能是使用延迟加载的最佳论据。

private void Form1_Load(object sender, EventArgs e)
{
    var treeNode = new TreeNode("Sea Drive");
    treeView1.Nodes.Add(treeNode);
    ThreadPool.QueueUserWorkItem(_ => TraverseDirectory("C:''", treeNode));
}
   
private static readonly string DirectorySeparatorString = Path.DirectorySeparatorChar.ToString();
private void TraverseDirectory(string initialDirectoryPath, TreeNode initialTreeNode)
{
    var initialTuples = new[] {Tuple.Create(initialDirectoryPath, initialTreeNode)};
    var directoryQueue = new Queue<Tuple<string, TreeNode>>(initialTuples);
    while (directoryQueue.Any())
    {
        var tuple = directoryQueue.Dequeue();
        var parentDirectoryPath = tuple.Item1;
        var parentTreeNode = tuple.Item2;
        try
        {
            var treeNodes = new List<TreeNode>();
            var directories = Directory.EnumerateDirectories(parentDirectoryPath);
            foreach (var directoryPath in directories)
            {
                var lastDirectorySeparator = directoryPath.LastIndexOf(DirectorySeparatorString);
                var directoryName = directoryPath.Substring(lastDirectorySeparator + 1);
                // Add the tree node to our list of child 
                // nodes, for an eventual call to AddRange
                var treeNode = new TreeNode(directoryName);
                treeNodes.Add(treeNode);
                // We have to go deeper
                directoryQueue.Enqueue(Tuple.Create(directoryPath, treeNode));
            }
            // Run this operation on the main thread
            Invoke((Action)(() => parentTreeNode.Nodes.AddRange(treeNodes.ToArray())));
        }
        catch (Exception exception)
        {
            Trace.Write(exception);
        }
    }
}

示例不完整;您需要提供自己的FormTreeView控件。