2

高速検索のためにすべての 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_Threads1. (files.Length = different number)私のアルゴリズムが間違っていることを証明する別の方法は、複数のスレッドがあるたびに常に異なる数のファイルを見つけることです。

4

1 に答える 1

7

これが問題です:

foreach ( var file in subDirAndFiles )
{

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

(膨大な数のスペースを除いて、私にとっては読むのが非常に困難です...型にはまらないメソッド名とともに。)

ラムダ式内でループ変数をキャプチャしています。つまり、(C# 3 および 4 では) 変数はラムダ式の実行時に評価されます。これは、後の反復から値を取得できることを意味します。

修正は、値をループ内の「新しい」変数にコピーすることです。

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

これは C# 5 コンパイラで変更されたため、「コピー」変数は必要ありません。代わりに、foreach反復ごとに新しい変数を宣言するかのようになり、より自然なアプローチになります。

于 2012-09-20T17:25:20.937 に答える