2

数日前、ディスクで高速検索を実行しようとしましたが、属性、拡張子、ファイル内の変更などのいくつかのことを行いました...

アイデアは、大きなファイルや内部にたくさんのファイルがあるディレクトリなどの「待ち時間」を避けるために、制限/ロックをほとんど持たないようにすることでした...私は使用していないので、「ベストプラクティス」にはほど遠いことを知っています「MaxDegreeOfParallelism」や「while(true)」を使用した Pulling ループなど

それでも、コードをサポートするアーキテクチャがあるため、コードは非常に高速に実行されます。

誰かが何が起こっているのかを確認したい場合は、コードをダミーのコンソール プロジェクトに移動しようとしました。

class Program
{
    static ConcurrentQueue<String> dirToCheck;
    static ConcurrentQueue<String> fileToCheck;
    static int fileCount; //

    static void Main(string[] args)
    {
        Initialize();

        Task.Factory.StartNew(() => ScanDirectories(), TaskCreationOptions.LongRunning);
        Task.Factory.StartNew(() => ScanFiles(), TaskCreationOptions.LongRunning);

        Console.ReadLine();
    }

    static void Initialize()
    {
        //Instantiate caches
        dirToCheck = new ConcurrentQueue<string>();
        fileToCheck = new ConcurrentQueue<string>();

        //Enqueue Directory to Scan here
        //Avoid to Enqueue Nested/Sub directories, else they are going to be dcan at least twice
        dirToCheck.Enqueue(@"C:\");

        //Initialize counters
        fileCount = 0;
    }

    static void ScanDirectories()
    {
        String dirToScan = null;

        while (true)
        {
            if (dirToCheck.TryDequeue(out dirToScan))
            {
                ExtractDirectories(dirToScan);
                ExtractFiles(dirToScan);
            }

            //Just here as a visual tracker to have some kind an idea about what's going on and where's the load
            Console.WriteLine(dirToCheck.Count + "\t\t" + fileToCheck.Count + "\t\t" + fileCount);
        }
    }

    static void ScanFiles()
    {
        while (true)
        {
            String fileToScan = null;
            if (fileToCheck.TryDequeue(out fileToScan))
            {
                CheckFileAsync(fileToScan);
            }
        }
    }

    private static Task ExtractDirectories(string dirToScan)
    {
        Task worker = Task.Factory.StartNew(() =>
        {
            try
            {
                Parallel.ForEach<String>(Directory.EnumerateDirectories(dirToScan), (dirPath) =>
                {
                    dirToCheck.Enqueue(dirPath);
                });

            }
            catch (UnauthorizedAccessException) { }
        }, TaskCreationOptions.AttachedToParent);

        return worker;
    }

    private static Task ExtractFiles(string dirToScan)
    {
        Task worker = Task.Factory.StartNew(() =>
        {
            try
            {
                Parallel.ForEach<String>(Directory.EnumerateFiles(dirToScan), (filePath) =>
                {
                    fileToCheck.Enqueue(filePath);
                });
            }
            catch (UnauthorizedAccessException) { }
        }, TaskCreationOptions.AttachedToParent);

        return worker;
    }

    static Task CheckFileAsync(String filePath)
    {
        Task worker = Task.Factory.StartNew(() =>
        {
            //Add statement to play along with the file here
            Interlocked.Increment(ref fileCount);


            //WARNING !!! If your file fullname is too long this code may not be executed or may just crash
            //I just put a simple check 'cause i found 2 or 3 different error message between the framework & msdn documentation
            //"Full paths must not exceed 260 characters to maintain compatibility with Windows operating systems. For more information about this restriction, see the entry Long Paths in .NET in the BCL Team blog"
            if (filePath.Length > 260)
                return;
            FileInfo fi = new FileInfo(filePath);

            //Add statement here to use FileInfo

        }, TaskCreationOptions.AttachedToParent);

        return worker;
    }
}

問題: ScanDirectory を使い終わったことをどのように検出できますか? 完了したら、空の文字列などをファイル キューにエンキューして終了することができます。「AttachedToParent」を使用すると、親タスクで完了状態を持つことができ、たとえば「ContinueWith(()=> { / SomeCode to notice the end /})」のようなことを行うことができますが、それでも親タスクプルを行っており、一種の無限ループに陥っており、各サブステートメントが新しいタスクを開始しています。

一方、ファイル リストとディレクトリ リストをフラッシュする可能性がありますが、「EnumerateDirectory()」を呼び出す別のタスクがある可能性があるため、各キューで「カウント」を単純にテストすることはできません。

私はある種の「リアクティブ」ソリューションを見つけようとしており、AsyncCall を使用した単純な while(true){} であるため、80% の時間で何もチェックされないループ内の「if()」を回避しようとしています。

PS: TPL データフローを使用できることはわかっていますが、TPL にはほとんど改善がないため、.net 4.5 でデータフローを使用せずに .net 4.0 に固執しているからではありません。まだ興味があります。

4

1 に答える 1

1

の代わりにConcurrentQueue<T>、を使用できますBlockingCollection<T>

BlockingCollection<T>このようなプロデューサー/コンシューマーシナリオ用に特別に設計されており、CompleteAddingメソッドを提供するため、プロデューサーは作業の追加が終了したことをコンシューマーに通知できます。

于 2012-10-16T23:43:51.883 に答える