2014-10-12 29 views
6

Ich arbeite an einem Dateisynchronisierungsdienst, um Dateien zwischen zwei Ordnern auf verschiedenen Computern zu synchronisieren. Ich brauche eine sehr schnelle Art und Weise zu finden, ein Verzeichnis und ziehen Sie die folgenden Informationen von ihm aufzuzählen:Schnellster Weg, um Verzeichnisdaten in .NET zu erhalten

  • Eine Datenstruktur oder Strukturen aller Dateipfade und Unterverzeichnispfade in diesem Verzeichnis, das die letzte Schreibzeit umfasst für jede Datei oder jedes Unterverzeichnis.
  • Für jedes Unterverzeichnis, das auf einer beliebigen Ebene unterhalb des aktuellen Verzeichnisses gefunden wird, dasselbe wie oben.

Bisher habe ich mit diesem kommen:

static void Main(string[] args) 
{ 
    List<Tuple<string, DateTime>> files = new List<Tuple<string, DateTime>>(); 
    List<Tuple<string, DateTime>> directories = new List<Tuple<string, DateTime>>(); 
    Stopwatch watch = new Stopwatch(); 
    while (true) 
    { 
     watch.Start(); 
     while (!CheckFolderRecursiveSingleThreaded("C:\\", out files, out directories)) 
     { 
      // You can assume for all intents and purposes that drive C does exist and that you have access to it, which will cause this sleep to not get called. 
      Thread.Sleep(1000); 
     } 
     watch.Stop(); 
     Console.WriteLine(watch.ElapsedMilliseconds); 
     watch.Reset(); 
     // Do something with the information. 
     Thread.Sleep(1000); 
    } 
} 

static bool CheckFolderRecursiveSingleThreaded(string path, out List<Tuple<string, DateTime>> files, out List<Tuple<string, DateTime>> directories) 
{ 
    try 
    { 
     DirectoryInfo directoryInformation = new DirectoryInfo(path); 
     List<Tuple<string, DateTime>> fileList = new List<Tuple<string, DateTime>>(); 
     foreach (FileInfo file in directoryInformation.GetFiles()) 
     { 
      fileList.Add(new Tuple<string, DateTime>(file.FullName, file.LastWriteTimeUtc)); 
     } 
     List<Tuple<string, DateTime>> directoryList = new List<Tuple<string, DateTime>>(); 
     foreach (DirectoryInfo directory in directoryInformation.GetDirectories()) 
     { 
      // Check for the ReparsePoint flag, which will indicate a symbolic link. 
      if (!directory.Attributes.HasFlag(FileAttributes.ReparsePoint)) 
      { 
       directoryList.Add(new Tuple<string, DateTime>(directory.FullName, directory.LastWriteTimeUtc)); 
       List<Tuple<string, DateTime>> directoryFiles; 
       List<Tuple<string, DateTime>> directoryFolders; 
       if (CheckFolderRecursiveSingleThreaded(directory.FullName, out directoryFiles, out directoryFolders)) 
       { 
        fileList.AddRange(directoryFiles); 
        directoryList.AddRange(directoryFolders); 
       } 
      } 
     } 
     files = fileList; 
     directories = directoryList; 
     return true; 
    } 
    catch 
    { 
     files = null; 
     directories = null; 
     return false; 
    } 
} 

Performance-weise, dauert es etwa 22 Sekunden (unabhängig davon, angebracht in Release oder Debug-Modus, ohne den Debugger ausgeführt wird) meine aufzuzählen durch C: \ Laufwerk und produzieren eine Liste von etwa 549.254 Dateien und 83.235 Ordner, auf die es Zugriff hat, aber kann es schneller sein? Ich bin offen für irgendwelche Vorschläge, sogar MSVC++ Vorschläge.

Bearbeiten: 12 Sekunden mit LINQ AsParallel wegen Multithreading (muss im Freigabemodus getestet werden). Beachten Sie, dass dies für alle C: \ -Unterordner parallelisiert wird, aber rekursive Aufrufe an die Singlethread-Implementierung, die ich oben habe, vorgenommen werden, sonst würde es SEHR lange dauern, für alle Ordner die ganze Zeit zu parallelisieren!

static bool CheckFolderParallelled(string path, out List<Tuple<string, DateTime>> files, out List<Tuple<string, DateTime>> directories) 
{ 
    try 
    { 
     DirectoryInfo directoryInformation = new DirectoryInfo(path); 
     List<Tuple<string, DateTime>> fileList = new List<Tuple<string, DateTime>>(); 
     foreach (FileInfo file in directoryInformation.GetFiles()) 
     { 
      fileList.Add(new Tuple<string, DateTime>(file.FullName, file.LastWriteTimeUtc)); 
     } 
     List<Tuple<string, DateTime>> directoryList = new List<Tuple<string, DateTime>>(); 
     directoryInformation.GetDirectories().AsParallel().ForAll(directory => 
     { 
      // Check for the ReparsePoint flag, which will indicate a symbolic link. 
      if (!directory.Attributes.HasFlag(FileAttributes.ReparsePoint)) 
      { 
       directoryList.Add(new Tuple<string, DateTime>(directory.FullName, directory.LastWriteTimeUtc)); 
       List<Tuple<string, DateTime>> directoryFiles; 
       List<Tuple<string, DateTime>> directoryFolders; 
       if (CheckFolderRecursiveSingleThreaded(directory.FullName, out directoryFiles, out directoryFolders)) 
       { 
        fileList.AddRange(directoryFiles); 
        directoryList.AddRange(directoryFolders); 
       } 
      } 
     }); 
     files = fileList; 
     directories = directoryList; 
     return true; 
    } 
    catch 
    { 
     files = null; 
     directories = null; 
     return false; 
    } 
} 

bearbeiten: Noch etwa 21 Sekunden Alexeis verknüpft-Lösung akzeptierte Antwort von Mark Gravell verwenden. Diese nicht-rekursive Technik ist nicht die schnellst (wahrscheinlich die Kosten für diesen Queue-Datentyp am Leben zu halten, ist genauso teuer wie die Kosten für Schub- und Anrufe an diese Methode auf dem Stack popping):

static bool CheckFolderNonRecursive(string path, out List<Tuple<string, DateTime>> files, out List<Tuple<string, DateTime>> directories) 
{ 
    try 
    { 
     List<Tuple<string, DateTime>> fileList = new List<Tuple<string, DateTime>>(); 
     List<Tuple<string, DateTime>> directoryList = new List<Tuple<string, DateTime>>(); 
     ConcurrentQueue<DirectoryInfo> pendingSearches = new ConcurrentQueue<DirectoryInfo>(); 
     pendingSearches.Enqueue(new DirectoryInfo(path)); 
     DirectoryInfo pendingDirectory; 
     while (pendingSearches.Count > 0) 
     { 
      if (pendingSearches.TryDequeue(out pendingDirectory)) 
      { 
       try 
       { 
        foreach (FileInfo file in pendingDirectory.GetFiles()) 
        { 
         fileList.Add(new Tuple<string, DateTime>(file.FullName, file.LastWriteTimeUtc)); 
        } 
        foreach (DirectoryInfo directory in pendingDirectory.GetDirectories()) 
        { 
         // Check for the ReparsePoint flag, which will indicate a symbolic link. 
         if (!directory.Attributes.HasFlag(FileAttributes.ReparsePoint)) 
         { 
          directoryList.Add(new Tuple<string, DateTime>(directory.FullName, directory.LastWriteTimeUtc)); 
          pendingSearches.Enqueue(directory); 
         } 
        } 
       } 
       catch { } // Ignore directories with no access rights. 
      } 
     } 
     files = fileList; 
     directories = directoryList; 
     return true; 
    } 
    catch 
    { 
     files = null; 
     directories = null; 
     return false; 
    } 
} 

bearbeiten: Diese Frage ist offen gegenüber .NET, weil MSVC++ -Bibliotheken wie Boost einen schnelleren Weg haben können, aber ich muss noch eine schnellere Methode finden. Wenn jemand meine C# -Methode mit einem schnelleren C-Laufwerk-Enumerator in C++ schlagen kann, der die gleichen Daten zieht, zunächst einmal ein großes Lob an Sie, weil ich es schneller machen würde, dann wäre ich wirklich daran interessiert, es zu sehen. Drittens würde es helfen eine Menge Leute (nicht nur ich selbst). Ich habe diese weit in Schub, bis ich das folgende Verfahren realisiert werden rund 200.000 ms dauerte, viel, viel länger als jeder Code, den ich habe geschrieben oben:

#include "stdafx.h" 
#include <iostream> 
#include <Windows.h> 
#include <boost/filesystem.hpp> 
#include <boost/foreach.hpp> 
#include <boost/timer.hpp> 

namespace fs = boost::filesystem; 

bool IterateDirectory(const wchar_t *directory); 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    boost::timer timer = boost::timer(); 
    while (true) 
    { 
     timer.restart(); 
     // L makes it wide, since IterateDirectory takes wchar_t. 
     // R makes it a raw string literal, which tells the compiler to parse the string as-is, not escape characters and fancy tricks. 
     IterateDirectory(LR"(C:\)"); 
     std::cout << "Elapsed time: " << timer.elapsed() * 1000 << " ms" << std::endl; 
     Sleep(1000); 
    } 
    return 0; 
} 

// IterateDirectory takes wchar_t because path.c_str() always returns wchar_t whether you are using unicode or multibyte. 
bool IterateDirectory(const wchar_t *directory) 
{ 
    if (boost::filesystem::exists(directory)) 
    { 
     fs::directory_iterator it(directory), eod; 
     BOOST_FOREACH(fs::path path, std::make_pair(it, eod)) 
     { 
      try 
      { 
       if (is_regular_file(path)) 
       { 
        //std::cout << path << ", last write time: " << last_write_time(path) << '.' << std::endl; 
       } 
       if (is_directory(path)) 
       { 
        //std::cout << path << ", last write time: " << last_write_time(path) << '.' << std::endl; 
        // path.c_str() always returns wchar_t, whether you are using unicode or multibyte. This is probably because of multi-language support inside of the Windows operating system and file structure. 
        IterateDirectory(path.c_str()); 
       } 
      } 
      catch (...) { } // Ignore directories we don't have access to. 
     } 
     return true; 
    } 
    return false; 
} 

bearbeiten: Mit PInvoke zu Findfirstfile und Findnextfile dauerte etwa 6 Sekunden, um mein gesamtes C-Laufwerk zu iterieren (dank des doppelten Links und der Antwort von Sam Saffron). Aber ... kann es schneller sein?

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
public static extern IntPtr FindFirstFileW(string lpFileName, out WIN32_FIND_DATAW lpFindFileData); 

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] 
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATAW lpFindFileData); 

[DllImport("kernel32.dll")] 
public static extern bool FindClose(IntPtr hFindFile); 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct WIN32_FIND_DATAW { 
    public FileAttributes dwFileAttributes; 
    internal System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; 
    internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; 
    internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; 
    public int nFileSizeHigh; 
    public int nFileSizeLow; 
    public int dwReserved0; 
    public int dwReserved1; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 
    public string cFileName; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] 
    public string cAlternateFileName; 
} 

static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); 

static bool FindNextFilePInvokeRecursive(string path, out List<Tuple<string, DateTime>> files, out List<Tuple<string, DateTime>> directories) 
{ 
    List<Tuple<string, DateTime>> fileList = new List<Tuple<string, DateTime>>(); 
    List<Tuple<string, DateTime>> directoryList = new List<Tuple<string, DateTime>>(); 
    WIN32_FIND_DATAW findData; 
    IntPtr findHandle = INVALID_HANDLE_VALUE; 
    List<Tuple<string, DateTime>> info = new List<Tuple<string,DateTime>>(); 
    try 
    { 
     findHandle = FindFirstFileW(path + @"\*", out findData); 
     if (findHandle != INVALID_HANDLE_VALUE) 
     { 
      do 
      { 
       if (findData.cFileName == "." || findData.cFileName == "..") continue; 
       string fullPath = path + (path.EndsWith("\\") ? String.Empty : "\\") + findData.cFileName; 
       // Check if this is a directory and not a symbolic link since symbolic links could lead to repeated files and folders as well as infinite loops. 
       if (findData.dwFileAttributes.HasFlag(FileAttributes.Directory) && !findData.dwFileAttributes.HasFlag(FileAttributes.ReparsePoint)) 
       { 
        directoryList.Add(new Tuple<string, DateTime>(fullPath, findData.ftLastWriteTime.ToDateTime())); 
        List<Tuple<string, DateTime>> subDirectoryFileList = new List<Tuple<string, DateTime>>(); 
        List<Tuple<string, DateTime>> subDirectoryDirectoryList = new List<Tuple<string, DateTime>>(); 
        if (FindNextFilePInvokeRecursive(fullPath, out subDirectoryFileList, out subDirectoryDirectoryList)) 
        { 
         fileList.AddRange(subDirectoryFileList); 
         directoryList.AddRange(subDirectoryDirectoryList); 
        } 
       } 
       else if (!findData.dwFileAttributes.HasFlag(FileAttributes.Directory)) 
       { 
        fileList.Add(new Tuple<string, DateTime>(fullPath, findData.ftLastWriteTime.ToDateTime())); 
       } 
      } 
      while (FindNextFile(findHandle, out findData)); 
     } 
    } 
    catch (Exception exception) 
    { 
     Console.WriteLine("Caught exception while trying to enumerate a directory. {0}", exception.ToString()); 
     if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
     files = null; 
     directories = null; 
     return false; 
    } 
    if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
    files = fileList; 
    directories = directoryList; 
    return true; 
} 

public static class FILETIMEExtensions 
{ 
    public static DateTime ToDateTime(this System.Runtime.InteropServices.ComTypes.FILETIME filetime) 
    { 
     long highBits = filetime.dwHighDateTime; 
     highBits = highBits << 32; 
     return DateTime.FromFileTimeUtc(highBits + (long)filetime.dwLowDateTime); 
    } 
} 

bearbeiten: Ja, es kann schneller sein. Mit Techniken zur Parallelisierung der Unterverzeichnis-Rekursionen des Zielordners kann ich sie mit der obigen FindNextFilePInvokeRecursive-Methode auf 4 Sekunden bringen. Das ist 4 Sekunden, um mein gesamtes C-Laufwerk mit den Daten zu iterieren, die ich brauche. Ich kann im Prozessmonitor sehen, ich esse etwa 30% CPU und nur 1% Festplatte höchstens, was ein bisschen merkwürdig für mich ist, nicht sicher, warum das im Moment ist, vielleicht nur diese verknüpfte Liste Traversal Stil verursacht es zu sein ziemlich vernachlässigbar.Idealerweise sollte es mindestens 100% CPU verbrauchen, aber das hängt möglicherweise von der Anzahl und Tiefe der Unterordner ab, auf denen Sie parallelisieren. Aber kann es schneller sein ?!

static bool FindNextFilePInvokeRecursiveParalleled(string path, out List<Tuple<string, DateTime>> files, out List<Tuple<string, DateTime>> directories) 
{ 
    List<Tuple<string, DateTime>> fileList = new List<Tuple<string, DateTime>>(); 
    List<Tuple<string, DateTime>> directoryList = new List<Tuple<string, DateTime>>(); 
    WIN32_FIND_DATAW findData; 
    IntPtr findHandle = INVALID_HANDLE_VALUE; 
    List<Tuple<string, DateTime>> info = new List<Tuple<string, DateTime>>(); 
    try 
    { 
     findHandle = FindFirstFileW(path + @"\*", out findData); 
     if (findHandle != INVALID_HANDLE_VALUE) 
     { 
      do 
      { 
       if (findData.cFileName == "." || findData.cFileName == "..") continue; 
       string fullPath = path + (path.EndsWith("\\") ? String.Empty : "\\") + findData.cFileName; 
       // Check if this is a directory and not a symbolic link since symbolic links could lead to repeated files and folders as well as infinite loops. 
       if (findData.dwFileAttributes.HasFlag(FileAttributes.Directory) && !findData.dwFileAttributes.HasFlag(FileAttributes.ReparsePoint)) 
       { 
        directoryList.Add(new Tuple<string, DateTime>(fullPath, findData.ftLastWriteTime.ToDateTime())); 
       } 
       else if (!findData.dwFileAttributes.HasFlag(FileAttributes.Directory)) 
       { 
        fileList.Add(new Tuple<string, DateTime>(fullPath, findData.ftLastWriteTime.ToDateTime())); 
       } 
      } 
      while (FindNextFile(findHandle, out findData)); 
      directoryList.AsParallel().ForAll(x => 
      { 
       List<Tuple<string, DateTime>> subDirectoryFileList = new List<Tuple<string, DateTime>>(); 
       List<Tuple<string, DateTime>> subDirectoryDirectoryList = new List<Tuple<string, DateTime>>(); 
       if (FindNextFilePInvokeRecursive(x.Item1, out subDirectoryFileList, out subDirectoryDirectoryList)) 
       { 
        fileList.AddRange(subDirectoryFileList); 
        directoryList.AddRange(subDirectoryDirectoryList); 
       } 
      }); 
     } 
    } 
    catch (Exception exception) 
    { 
     Console.WriteLine("Caught exception while trying to enumerate a directory. {0}", exception.ToString()); 
     if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
     files = null; 
     directories = null; 
     return false; 
    } 
    if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
    files = fileList; 
    directories = directoryList; 
    return true; 
} 

bearbeiten: Vergessen Gleichzeitigkeit Sperren hinzuzufügen, wenn Parallelen verwenden, sonst könnte man eine Ausnahme zu fangen. Außerdem wurden Tupel entfernt und ich habe eine FileInformation/DirectoryInformation-Klasse für meine Zwecke verwendet. Dies wurde um 0,5 Sekunden verkürzt. Jetzt 3,5 Sekunden, um mein Laufwerk C: aufzuzählen.

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] 
public static extern IntPtr FindFirstFileW(string lpFileName, out WIN32_FIND_DATAW lpFindFileData); 

[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] 
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATAW lpFindFileData); 

[DllImport("kernel32.dll")] 
public static extern bool FindClose(IntPtr hFindFile); 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 
public struct WIN32_FIND_DATAW { 
    public FileAttributes dwFileAttributes; 
    internal System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime; 
    internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime; 
    internal System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime; 
    public int nFileSizeHigh; 
    public int nFileSizeLow; 
    public int dwReserved0; 
    public int dwReserved1; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] 
    public string cFileName; 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)] 
    public string cAlternateFileName; 
} 

static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); 

static bool FindNextFilePInvokeRecursive(string path, out List<FileInformation> files, out List<DirectoryInformation> directories) 
{ 
    List<FileInformation> fileList = new List<FileInformation>(); 
    List<DirectoryInformation> directoryList = new List<DirectoryInformation>(); 
    WIN32_FIND_DATAW findData; 
    IntPtr findHandle = INVALID_HANDLE_VALUE; 
    List<Tuple<string, DateTime>> info = new List<Tuple<string, DateTime>>(); 
    try 
    { 
     findHandle = FindFirstFileW(path + @"\*", out findData); 
     if (findHandle != INVALID_HANDLE_VALUE) 
     { 
      do 
      { 
       // Skip current directory and parent directory symbols that are returned. 
       if (findData.cFileName != "." && findData.cFileName != "..") 
       { 
        string fullPath = path + @"\" + findData.cFileName; 
        // Check if this is a directory and not a symbolic link since symbolic links could lead to repeated files and folders as well as infinite loops. 
        if (findData.dwFileAttributes.HasFlag(FileAttributes.Directory) && !findData.dwFileAttributes.HasFlag(FileAttributes.ReparsePoint)) 
        { 
         directoryList.Add(new DirectoryInformation { FullPath = fullPath, LastWriteTime = findData.ftLastWriteTime.ToDateTime() }); 
         List<FileInformation> subDirectoryFileList = new List<FileInformation>(); 
         List<DirectoryInformation> subDirectoryDirectoryList = new List<DirectoryInformation>(); 
         if (FindNextFilePInvokeRecursive(fullPath, out subDirectoryFileList, out subDirectoryDirectoryList)) 
         { 
          fileList.AddRange(subDirectoryFileList); 
          directoryList.AddRange(subDirectoryDirectoryList); 
         } 
        } 
        else if (!findData.dwFileAttributes.HasFlag(FileAttributes.Directory)) 
        { 
         fileList.Add(new FileInformation { FullPath = fullPath, LastWriteTime = findData.ftLastWriteTime.ToDateTime() }); 
        } 
       } 
      } 
      while (FindNextFile(findHandle, out findData)); 
     } 
    } 
    catch (Exception exception) 
    { 
     Console.WriteLine("Caught exception while trying to enumerate a directory. {0}", exception.ToString()); 
     if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
     files = null; 
     directories = null; 
     return false; 
    } 
    if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
    files = fileList; 
    directories = directoryList; 
    return true; 
} 

static bool FindNextFilePInvokeRecursiveParalleled(string path, out List<FileInformation> files, out List<DirectoryInformation> directories) 
{ 
    List<FileInformation> fileList = new List<FileInformation>(); 
    object fileListLock = new object(); 
    List<DirectoryInformation> directoryList = new List<DirectoryInformation>(); 
    object directoryListLock = new object(); 
    WIN32_FIND_DATAW findData; 
    IntPtr findHandle = INVALID_HANDLE_VALUE; 
    List<Tuple<string, DateTime>> info = new List<Tuple<string, DateTime>>(); 
    try 
    { 
     path = path.EndsWith(@"\") ? path : path + @"\"; 
     findHandle = FindFirstFileW(path + @"*", out findData); 
     if (findHandle != INVALID_HANDLE_VALUE) 
     { 
      do 
      { 
       // Skip current directory and parent directory symbols that are returned. 
       if (findData.cFileName != "." && findData.cFileName != "..") 
       { 
        string fullPath = path + findData.cFileName; 
        // Check if this is a directory and not a symbolic link since symbolic links could lead to repeated files and folders as well as infinite loops. 
        if (findData.dwFileAttributes.HasFlag(FileAttributes.Directory) && !findData.dwFileAttributes.HasFlag(FileAttributes.ReparsePoint)) 
        { 
         directoryList.Add(new DirectoryInformation { FullPath = fullPath, LastWriteTime = findData.ftLastWriteTime.ToDateTime() }); 
        } 
        else if (!findData.dwFileAttributes.HasFlag(FileAttributes.Directory)) 
        { 
         fileList.Add(new FileInformation { FullPath = fullPath, LastWriteTime = findData.ftLastWriteTime.ToDateTime() }); 
        } 
       } 
      } 
      while (FindNextFile(findHandle, out findData)); 
      directoryList.AsParallel().ForAll(x => 
      { 
       List<FileInformation> subDirectoryFileList = new List<FileInformation>(); 
       List<DirectoryInformation> subDirectoryDirectoryList = new List<DirectoryInformation>(); 
       if (FindNextFilePInvokeRecursive(x.FullPath, out subDirectoryFileList, out subDirectoryDirectoryList)) 
       { 
        lock (fileListLock) 
        { 
         fileList.AddRange(subDirectoryFileList); 
        } 
        lock (directoryListLock) 
        { 
         directoryList.AddRange(subDirectoryDirectoryList); 
        } 
       } 
      }); 
     } 
    } 
    catch (Exception exception) 
    { 
     Console.WriteLine("Caught exception while trying to enumerate a directory. {0}", exception.ToString()); 
     if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
     files = null; 
     directories = null; 
     return false; 
    } 
    if (findHandle != INVALID_HANDLE_VALUE) FindClose(findHandle); 
    files = fileList; 
    directories = directoryList; 
    return true; 
} 

public class FileInformation 
{ 
    public string FullPath; 
    public DateTime LastWriteTime; 
} 

public class DirectoryInformation 
{ 
    public string FullPath; 
    public DateTime LastWriteTime; 
} 

bearbeiten: B. K. über die Umstellung auf Datetime von FILE fragt:

public static class FILETIMEExtensions 
{ 
    public static DateTime ToDateTime(this System.Runtime.InteropServices.ComTypes.FILETIME time) 
    { 
     ulong high = (ulong)time.dwHighDateTime; 
     ulong low = (ulong)time.dwLowDateTime; 
     long fileTime = (long)((high << 32) + low); 
     return DateTime.FromFileTimeUtc(fileTime); 
    } 
} 
+0

@AlexeiLevenkov Ich glaube, wenn ich Geschwindigkeiten erhöhen kann mit LINQ Multithreading fast doppelt so schnell zu sein, dann es kann viel mehr Raum für Verbesserungen geben, und ich nenne diese Frage, um wieder geöffnet zu werden, zum Wohle der Menschheit. :) – Alexandru

+0

@AlexeiLevenkov Ich glaube nicht, dass es ein Duplikat ist, weil ich für die letzte Schreibzeit statt Erweiterungstyp abfragen muss. Nehmen Sie Mark Gravells akzeptierte Lösung in der Frage, die Sie zum Beispiel verknüpft haben, es verwendet Directory.GetFiles oder Directory.GetDirectories, die Strings zurückgeben, überhaupt nicht die Daten, die ich brauche, und wenn Sie es ändern, um einen nicht-rekursiven Ansatz zu verwenden .GetFiles/Direct, es dauert etwa 22 Sekunden (immer noch das gleiche wie meine erste, rekursive Implementierung) im Freigabemodus, wahrscheinlich weil die Warteschlange aktiv bleiben muss und die Kosten für das Einreihen/Entfernen der Warteschlange. – Alexandru

+0

@AlexeiLevenkov Ich habe die Frage aktualisiert. Außerdem bin ich mir nicht sicher, ob es hier ein Missverständnis gibt, aber als ich sagte, dass ich versuche, Ordner auf zwei Computern zu synchronisieren, habe ich nie gesagt, dass sie sich im selben Netzwerk befinden. Tatsächlich wird die Aufzählungsoperation, nach der ich frage, durchgeführt, und dann sollten zwei WCF-Dienste Dateien zwischen den beiden Maschinen vergleichen. Ich versuche nur, die Ordneraufzählung so schnell wie möglich zu machen. – Alexandru

Antwort

1

Verwendung von LINQ und paralleler Tasks

var stuff = dir.GetFiles("*.*", System.IO.SearchOption.AllDirectories); 
Parallel.ForEach(stuff, p=>{ //do things in parrallel.. }); 
//or this 
var q = stuff.AsParallel().Where(x => p(x)).Orderby(x => k(x)).Select(x => f(x));  foreach (var e in q) a(e); 
+0

Und wie wird LINQ die Leistung steigern? – mybirthname

+1

Guter Punkt ... Verwenden Sie Parallel.ForEach, aber es dauert die LINQ Enumerables. In Ihrem Fall bevölkern Sie die Tupel. Muss das nicht tun. Aber das Hinzufügen eines Leistungsmessungswerkzeugs ist am besten, um beide Lösungen zu beweisen. Schließlich, wenn Sie wirklich schnell brauchen, schreiben Sie die Anwendung in C++. –

+0

Ich werde versuchen, zu sehen, wenn ich Zeit habe, aber es gibt ein Problem mit dieser Methode: Wenn Sie AllDirectories in Ihrer Suche auswählen und die Verzeichnisstruktur eine Verknüpfung (z. B. eine symbolische Verknüpfung zu einem anderen Ordner) enthält, die eine Schleife erstellt Suchoperation tritt in eine Endlosschleife ein. – Alexandru

Verwandte Themen