1

現在、約 170,000 の jpg ファイル名の .txt ファイルがあり、それらすべてをリスト (fileNames) に読み込みます。

1 つのフォルダー (このフォルダーにはサブフォルダーがあります) を検索して、fileNames の各ファイルがこのフォルダーに存在するかどうかを確認し、存在する場合は新しいフォルダーにコピーします。

大まかな見積もりを作成しましたが、fileNames の各ファイル名の各検索とコピーには約 0.5 秒かかります。したがって、170,000 秒はおよそ 48 時間なので、2 で割ると、アプリが 1 つのスレッドを使用してすべてのファイル名を検索するのに約 24 時間かかります! 明らかにこれは長すぎるので、これを絞り込んでプロセスをスピードアップしたいと思います。マルチスレッドを使用してこれを行うための最良の方法は何ですか?

現在、20 の個別のスレッドを作成し、リスト (fileNames) を 20 の異なるリストに分割して、ファイルを同時に検索することを考えていました。たとえば、以下を同時に実行する 20 の異なるスレッドがあるとします。

            foreach (string str in fileNames)
            {
                foreach (var file in Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories))
                {
                    string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file));
                    if (!File.Exists(combinedPath))
                    {
                        File.Copy(file, combinedPath);
                    }
                }
            }

以下の私の解決策を示すために更新されました:

            string[] folderToCheckForFileNames = Directory.GetFiles("C:\\Users\\Alex\\Desktop\\ok", "*.jpg", SearchOption.AllDirectories);

            foreach(string str in fileNames)
            {
                Parallel.ForEach(folderToCheckForFileNames, currentFile =>
                  {
                      string filename = Path.GetFileName(currentFile);
                      if (str == filename)
                      {
                          string combinedPath = Path.Combine(targetDir, filename);
                          if (!File.Exists(combinedPath))
                          {
                              File.Copy(currentFile, combinedPath);
                              Console.WriteLine("FOUND A MATCH AND COPIED" + currentFile);
                          }
                      }

                  }
                );

            }

皆様、ご協力ありがとうございました!大感謝!

4

2 に答える 2

0

コンピューターのコア数が 20 未満の場合、20 の異なるスレッドは役に立ちません。実際、プロセスが遅くなる可能性があります。これは、1) 各スレッド間のコンテキスト切り替えに時間を費やす必要があり (これは、CPU が複数のスレッド/コアをエミュレートする方法です)、2) Thread.NET ではそのために 1 MB を予約するためです。スタック、これはかなり重いです。

代わりに、I/O をasyncワークロードに分割しTask.Runて、CPU バウンド / 集中的な部分に使用してみてください。また、Tasks最大で 4 から 8 までの数を維持してください。

サンプルコード:

var tasks = new Task[8];
var names = fileNames.ToArray();
for (int i = 0; i < tasks.Length; i++)
{
    int index = i;
    tasks[i] = Task.Run(() =>
    {
        for (int current = index; current < names.Length; current += 8)
        {
            // execute the workload
            string str = names[current];
            foreach (var file in Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories))
            {
                string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file));
                if (!File.Exists(combinedPath))
                {
                    File.Copy(file, combinedPath);
                }
            }
        }
    });
}
Task.WaitAll(tasks);
于 2015-06-22T04:29:13.083 に答える
0

検索を行う際に通常の foreach ステートメントを使用する代わりに、parallel linq を使用する必要があります。Parallel linq は、LINQ 構文の単純さと読みやすさを、並列プログラミングの能力と組み合わせたものです。タスク並列ライブラリを対象とするコードと同様です。これにより、作業を多くのスレッドに分割しながら、低レベルのスレッド操作と可能性のある例外 (見つけにくい/デバッグする例外) から保護されます。したがって、次のようなことができます。

fileNames.AsParallel().ForAll(str =>
            {
                var files = Directory.GetFiles(folderToCheckForFileName, str, SearchOption.AllDirectories);
                files.AsParallel().ForAll(file =>
                {
                    if (!string.IsNullOrEmpty(file))
                    {
                        string combinedPath = Path.Combine(newTargetDirectory, Path.GetFileName(file));
                        if (!File.Exists(combinedPath))
                        {
                            File.Copy(file, combinedPath);
                        }
                    }
                });
            });
于 2015-06-22T06:04:48.077 に答える