目录枚举第一次太慢

时间:2013-07-06 15:50:54

标签: c# performance

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

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秒的时间。帮助我如何改进枚举。这是使用线程和部分更新。如果以后不能修复,请告诉我原因。

2 个答案:

答案 0 :(得分:5)

除了枚举系统中的所有目录所花费的时间(如果实施lazy loading,您可能会看到更好的性能),将项目插入TreeView会占用大量的时间。

来自TreeView.BeginUpdate

  

要在将项目一次添加到TreeView时保持性能,请调用BeginUpdate方法。 BeginUpdate方法阻止控件绘制,直到调用EndUpdate方法。 将项添加到树视图控件的首选方法是使用AddRange方法将树节点项数组添加到树视图中。

     

...

     

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

虽然它脱离了.NET,但Raymond Chen的博客文章How to insert a large number of items into a treeview efficiently提供了更多信息,可以帮助您以更好的项目插入性能的方式构建代码。

  

如果您需要将大量项目插入到树视图中,例如成千上万,那么将它们“向后”插入它们会更有效。

修改

这是一个将目录枚举放到线程上的示例。观察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控件。

答案 1 :(得分:5)

除了有关改善TreeView填充调用的先前答案之外,您还应该阅读MSDN页面How to: Enumerate Directories and Files

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

  

您可以使用返回的方法枚举目录和文件   一个可枚举的名字字符串集合。你也可以使用   返回DirectoryInfo的可枚举集合的方法,   FileInfo或FileSystemInfo对象。提供了可枚举的集合   使用大型集合时,性能优于数组   目录和文件。

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

如果这两个改进仍然没有给你你想要的性能,那么你可能想要增加更多的复杂性(例如运行后台线程来执行遍历),但你会想要确切地知道您的代码的哪一部分首先是您的瓶颈(意味着您将要添加时序代码和日志记录)