【C#】【ファイル検索高速化】ファイル検索が遅いと感じる方へ！高速でファイル検索を行うファイル検索ツールをC#で作ってみた

Windowsの検索バーで行うファイル検索は、遅いと感じたことはありませんか？

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/283871/9d1813e8-ca5e-3233-d11c-175b255ab65e.png)

それもそのはず、ファイルは階層構造になっていて、検索するには奥深くまで全て調べなければいけません。

そこで、ファイル検索を非同期で並列検索して高速化できないか考えました。

# Taskクラスの使用
ファイルシステムなどツリー構造になっているものはプログラム上で「再帰呼び出し」という方法を使って探索します。

そこで、Taskクラスを使用して再帰呼び出しの部分を非同期で行うことにしました。

以下にコードを示します。

```FrmMain.cs
        /// <summary>
        ///     ファイル検索を行う。
        /// </summary>
        /// <param name="strFileName">ファイル名</param>
        /// <param name="strDirOrFile">現在探索中のディレクトリまたはファイル</param>
        /// <param name="blnIgnoreDir">フォルダパスを無視する</param>
        /// <param name="kind">ファイルまたはフォルダ</param>
        private void SearchFile(string strFileName, string strDirOrFile, bool blnIgnoreDir, FileFolder kind)
        {
            if (this.blnCancel) return;

            Action action = () =>
            {
                Interlocked.Increment(ref this.intRunCount);
                try
                {
                    if (kind == FileFolder.File)
                    {
                        string strTarget = strDirOrFile;
                        if (blnIgnoreDir) strTarget = System.IO.Path.GetFileName(strTarget);
                        if (this.IsMatch(strFileName, strTarget))
                        {
                            this.context.Post(d =>
                            {
                                DgvRowFilePath rowBound = new DgvRowFilePath() { FilePath = strDirOrFile };
                                this.lstList.Add(rowBound);
                            }, null);
                        }
                        Interlocked.Increment(ref this.intFileCount);
                    }
                    else
                    {
                        this.context.Post(d =>
                        {
                            if (this.blnCancel) return;
                            this.lblStatus.Text = this.intFileCount + "ファイル検索済み 探索中：" + strDirOrFile.Replace(this.txtBaseDir.Text + "\\", "");
                            this.lblTime.Text = (this.sw.ElapsedMilliseconds / 1000) + "秒経過";
                        }, null);
                        string[] aryFiles = new string[0];
                        string[] aryFolders = new string[0];
                        try { aryFiles = System.IO.Directory.GetFiles(strDirOrFile); } catch (Exception) { }
                        try { aryFolders = System.IO.Directory.GetDirectories(strDirOrFile); } catch (Exception) { }
                        foreach (string file in aryFiles) this.SearchFile(strFileName, file, blnIgnoreDir, FileFolder.File);
                        foreach (string folder in aryFolders) this.SearchFile(strFileName, folder, blnIgnoreDir, FileFolder.Folder);
                    }
                }
                finally
                {
                    Interlocked.Decrement(ref this.intRunCount);
                }
            };

            Task.Run(action);
        }
```

ファイル検索のメソッド自体を非同期処理の対象とします。ディレクトリがあればそのディレクトリ内を別のスレッドで探索処理します。さらにその中でディレクトリがあればさらに別のスレッドで処理を行います。

見つかったファイル数```this.intFileCount```は非同期の場合インクリメント（```++```）するとスレッドセーフにならないため、```System.Threading```名前空間の```Interlocked.Increment```メソッドを使用してインクリメントします。

このプログラムではスレッドの実行数```this.intRunCount```を保持しており、簡易的な処理としてスレッドの実行数が0になったタイミングで検索結果を表示するようにしています。

# 最後に
ファイル検索が遅く悩んでいる方が、本記事により、お役立ちいただければ幸いです。
また、次のリンクにてソースを公開しておりますので、参考にして改良していただければ幸いです。ソース自体は自由に使用して頂いて構いません。

GitHubのリンクはこちらになります。
https://github.com/koshiRyu/FileExplorer

最後まで見てくださりありがとうございます。