c# - 为什么递归算法会添加重复文件?

标签 c# multithreading file task-parallel-library

一旦我为我的所有 C 驱动器建立了索引以进行快速搜索,使用多个线程来搜索文件会大有帮助!唯一的问题是我发现了重复的文件。如果有人能解释我为什么在以下算法中有重复文件,我将不胜感激。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static volatile List<System.IO.FileSystemInfo> files = new List<System.IO.FileSystemInfo> ( );
    static readonly object lockFiles = new object ( );  // every time we read or write to files we use this lock

    static long numberOfThreadsRuning; // used to determine when we are done. if 0 threads runing it means we are done!

    static readonly int max_Number_OF_Threads = 8; 

    static void Main (  )
    {
        numberOfThreadsRuning = 0;

        // add first task this will increase numberOfThreadsRuning
        AddTask ( () => findFiles ( @"C:\Users\Antonio" ) );

        // while number of threads running is greater than 0
        while ( Interlocked.Read ( ref numberOfThreadsRuning ) > 0 )   
            Thread.Sleep ( 100 );            
    }

    // start finding files in a new thread
    static void AddTask (Action task )
    {
        // increment numberOfThreadsRuning with a lock
        Interlocked.Increment ( ref numberOfThreadsRuning );

        Task.Factory.StartNew ( task ).ContinueWith ( x =>
        {
            // once we are done executing the task decrement number of threads runing
            Interlocked.Decrement ( ref numberOfThreadsRuning );
        });
    }

    // recursively start finding files
    static void findFiles ( string path )
    {
        System.IO.FileSystemInfo[ ] subDirAndFiles;

        try {
            subDirAndFiles = new System.IO.DirectoryInfo (path).GetFileSystemInfos ( );
        }
        catch { // sometimes we are not able to access some directories so just ignore the error
            return;
        }

        foreach ( var file in subDirAndFiles )
        {
            lock(lockFiles)
                files.Add ( file );

            if ( file is System.IO.DirectoryInfo ) // if it is a directory
            {
                var numTaskRun = Interlocked.Read (ref  numberOfThreadsRuning );

                if ( numTaskRun < max_Number_OF_Threads )
                    AddTask ( ( ) => findFiles ( file.FullName ) ); // if there are 8 or less threads running create a new task
                else
                    findFiles ( file.FullName ); // otherwise continue finding files in current thread
            }                
        }
    }
}

请通过将静态变量 max_Number_OF_Threads 更改为 1 来尝试比较当我使用 8 线程 VS 1 时它的速度有多快。另一种证明我的算法错误的方法,每次我有一个以上的线程时,它总是找到不同数量的文件 (files.Length = different number)

最佳答案

问题是:

foreach ( var file in subDirAndFiles )
{

    ...
    AddTask ( ( ) => findFiles ( file.FullName ) );
}

(除了大量的空格,这让我很难阅读……还有非常规的方法名称。)

您正在捕获 lambda 表达式中的循环变量。这意味着(在 C# 3 和 4 中)变量将在 执行 lambda 表达式时 求值 - 这意味着您可以从稍后的迭代中获取值。

修复方法是将值复制到循环内的"new"变量中:

foreach (var file in subDirAndFiles)
{
    var copy = file;
    AddTask(() => FindFiles(copy.FullName));
}

这在 C# 5 编译器中已更改,因此您不需要“复制”变量 - 相反,就好像 foreach 在每次迭代中声明一个新变量,这是一个很大的更自然的方法。

关于c# - 为什么递归算法会添加重复文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12517557/

相关文章:

c# - 为什么我不能点击我的表格?

json - 解码后的 JSON 字段仅给出 nil 值,无法存储到变量中

javascript - 使用本地文件动态设置音频 src

multithreading - 线程和进程

c# - 如果父应用程序未释放互斥锁对象会发生什么

java - 在 Java 中将 long(原始类型)写入文件

javascript - 如何在外部文件中创建选择菜单并将其嵌入到 html 中以显示菜单

c# - H结果 : 0x800A03EC - It works on my machine issue when creating Excel files

c# - 回发后保留值

c# - 创建一个支持不同单位(摄氏度、华氏度、开尔文)的温度类