3

私が尋ねた質問では、フォルダとすべてのサブフォルダ内のファイル名のリストをすばやく取得します。他にもいくつか見つけましたが、多くのファイルを検索する方法は、EnumerateFilesメソッドを使用することです。

EnumerateFilesメソッドとGetFilesメソッドは、次のように異なります。EnumerateFilesを使用すると、コレクション全体が返される前に、名前のコレクションの列挙を開始できます。GetFilesを使用する場合、配列にアクセスする前に、名前の配列全体が返されるのを待つ必要があります。したがって、多くのファイルやディレクトリを操作している場合は、EnumerateFilesの方が効率的です。

これは私にとって素晴らしいことのように聞こえます。検索には約10秒かかるので、情報が入ってくるとリストを作成し始めることができます。しかし、それを理解することはできません。EnumerateFilesメソッドを実行すると、アプリケーションが完了するまでフリーズします。バックグラウンドワーカーで実行することもできますが、そのスレッドでも同じことが起こります。何か助けはありますか?

 DirectoryInfo dir = new DirectoryInfo(MainFolder);
 List<FileInfo> matches = new List<FileInfo>(dir.EnumerateFiles("*.docx",SearchOption.AllDirectories));

//This wont fire until after the entire collection is complete
DoSoemthingWhileWaiting();
4

3 に答える 3

12

これを行うには、バックグラウンドタスクにプッシュします。

たとえば、次のことができます。

var fileTask = Task.Factory.StartNew( () =>
{
    DirectoryInfo dir = new DirectoryInfo(MainFolder);
    return new List<FileInfo>(
           dir.EnumerateFiles("*.docx",SearchOption.AllDirectories)
           .Take(200) // In previous question, you mentioned only wanting 200 items
       );
};

// To process items:
fileTask.ContinueWith( t =>
{
     List<FileInfo> files = t.Result;

     // Use the results...
     foreach(var file in files)
     {
         this.listBox.Add(file); // Whatever you want here...
     }
}, TaskScheduler.FromCurrentSynchronizationContext()); // Make sure this runs on the UI thread

DoSomethingWhileWaiting();

あなたはコメントで言及しました:

それらをリストに表示したい。入ってくるときにメインUIに完璧に送信します

この場合、それらをバックグラウンドで処理し、入ってくるときにリストに追加する必要があります。次のようなものです。

Task.Factory.StartNew( () =>
{
    DirectoryInfo dir = new DirectoryInfo(MainFolder);
    foreach(var tmp in dir.EnumerateFiles("*.docx",SearchOption.AllDirectories).Take(200))
    {
        string file = tmp; // Handle closure issue

        // You may want to do this in batches of >1 item...
        this.BeginInvoke( new Action(() =>
        {
             this.listBox.Add(file);
        }));
    }
});
DoSomethingWhileWaiting();
于 2012-05-15T17:23:48.767 に答える
7

のコンストラクターに配置することでこの列挙可能なものを消費しているため、アプリケーションがフリーズList<FileInfo>します。古いメソッドを熱心に呼び出しているかのようです。したがって、これを行う適切な方法は、バックグラウンドスレッド内で実行し、結果をすぐに最後まで列挙可能なものを消費するものに渡さず、ループに到着したときに反復を開始してアイテムを追加することです。

バックグラウンドワーカーで実行することもできますが、そのスレッドでも同じことが起こります。

はい、ループ中に明らかにバックグラウンドスレッドをフリーズしますが、それがバックグラウンドスレッドの目的です。メインUIスレッドのフリーズを回避するためです。アイテムをメインスレッドに渡してアイテムを表示するときは、UIとの適切な同期を使用していることを確認してください。WinFormsでは、これはControl.Invokeメソッドで発生します。もちろん、バックグラウンドスレッドとUIスレッドの間で頻繁にマーシャリングを行うと、アプリケーションがフリーズしたように感じる可能性があるため、注意が必要です。これを回避するには、ファイルがチャンクで到着したときにファイルを渡すことができます。

次に例を示します。

Task.Factory.StartNew(() =>
{
    var dir = new DirectoryInfo(MainFolder);
    var files = dir.EnumerateFiles("*.docx", SearchOption.AllDirectories);
    foreach (var file in files)
    {
        Action<string> del = f => listBox1.Items.Add((string)f);
        BeginInvoke(del, file);
    }
});
于 2012-05-15T17:15:43.140 に答える
4

できるよ

    forach(var f in dir.EnumerateFiles("*.docx",SearchOption.AllDirectories))
    {
//.. process file
    }

スレッド内で、プロセスの完了後にイベントを発生させます

于 2012-05-15T17:16:50.110 に答える