2014-10-12 10 views
6

Sto lavorando a un servizio di sincronizzazione file per sincronizzare i file tra due cartelle su macchine diverse. Ho bisogno di trovare un modo molto veloce per enumerare una directory ed estrarre le seguenti informazioni da esso:Il modo più veloce per ottenere i dati della directory in .NET

  • Una struttura di dati o strutture di tutti i percorsi di file e percorsi sub-directory all'interno di questa directory, che include l'ultima volta scrittura per ogni file o sottodirectory.
  • Per ogni sottodirectory che si trova a qualsiasi livello al di sotto della directory corrente, come sopra.

Finora, sono venuto su con questo:

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; 
    } 
} 

delle prestazioni, ci vogliono circa 22 secondi (a prescindere dalla esecuzione in modalità di debug di rilascio o senza il debugger allegato) per enumerare attraverso il mio C: \ drive e produce un elenco di circa 549.254 file e 83.235 cartelle a cui ha accesso, ma può essere più veloce? Sono aperto a qualsiasi suggerimento, anche a suggerimenti MSVC++.

Modifica: 12 secondi con AsParallel di LINQ a causa del multithreading (deve essere testato in modalità di rilascio). Notare che questo è in parallelo per tutte le sottocartelle C: \, ma le chiamate ricorsive saranno fatte all'implementazione a thread singolo che ho sopra, altrimenti ci vorrebbe un tempo VERAMENTE lungo per parallelizzare tutte le cartelle per tutto il tempo!

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; 
    } 
} 

Edit: Ancora circa 21 secondi con risposta accettata della soluzione legata di Alexei da Mark Gravell. Questa tecnica non ricorsiva non è il più veloce (probabilmente il costo di mantenere questo tipo di dati della coda vivo è altrettanto costoso come il costo di spingere e popping chiamate a questo metodo nello stack):

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; 
    } 
} 

Edit: Questa domanda è aperta verso .NET, perché potrebbe esserci un modo più veloce con le librerie MSVC++ come boost, ma devo ancora trovare un metodo più veloce. Se qualcuno può battere il mio metodo C# con un enumeratore di C drive più veloce in C++ che tira gli stessi dati, prima di tutto complimenti a te per farlo più velocemente, in secondo luogo sarei davvero interessato a vederlo, terzo di tutto sarebbe di aiuto un sacco di gente fuori (non solo me stesso). Ho ottenuto questo lontano nella spinta fino a quando ho capito il seguente metodo ha avuto circa 200.000 ms, molto, molto di più di qualsiasi codice che ho postato sopra:

#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; 
} 

Edit: Utilizzo PInvoke per FindFirstFile e FindNextFile preso circa 6 secondi per iterare la mia intera unità C (grazie al collegamento duplicato e alla risposta di Sam Saffron). Ma ... può essere più veloce?

[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); 
    } 
} 

Edit: Sì, può essere più veloce. Usando tecniche per parallelizzare le ricorsioni di sottodirectory della cartella di destinazione, posso ottenerlo a 4 secondi usando il metodo FindNextFilePInvokeRecursive sopra. Quello è 4 secondi per iterare la mia intera unità C con i dati che mi servono. Riesco a vedere nel monitor di processo, mangio circa il 30% di CPU e solo l'1% di disco al massimo, il che è un po 'strano per me, non so perché sia ​​in questo momento, forse solo questo stile di attraversamento di liste collegate fa sì che sia piuttosto trascurabile.Idealmente dovrebbe mangiare almeno il 100% della CPU, ma ciò potrebbe dipendere dal numero e dalla profondità delle sottocartelle su cui si esegue la parallelizzazione. Ma può essere più veloce ?!

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; 
} 

Edit: Ho dimenticato di aggiungere blocchi di concorrenza quando si utilizza paralleli, altrimenti si potrebbe intercettare un'eccezione. Inoltre tuple rimosse e sono andato con una classe FileInformation/DirectoryInformation per i miei scopi. Questo rasato .5 secondi. Ora 3,5 secondi per enumerare la mia C: unità.

[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; 
} 

Edit: B.K. è stato chiesto circa la conversione a DateTime da FILETIME:

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 Penso che se posso aumentare la velocità per essere quasi due volte più veloce utilizzando il multithreading di LINQ, poi ci può essere molto più spazio per migliorare, e sto nominando questa domanda da riaprire, per il bene dell'umanità. :) – Alexandru

+0

@AlexeiLevenkov Non penso che sia un duplicato perché ho bisogno di interrogare per l'ultima volta in scrittura invece del tipo di estensione. Prendi la soluzione accettata di Mark Gravell nella domanda che hai collegato, ad esempio, utilizza Directory.GetFiles o Directory.GetDirectories che restituiscono le stringhe, non tutti i dati che mi servono e se lo modifichi per utilizzare un approccio basato non ricorsivo usando .GetFiles/Direct, richiede circa 22 secondi (sempre uguale alla mia prima implementazione ricorsiva) nella modalità di rilascio, probabilmente a causa del mantenimento della coda attiva e del costo di accodamento/dequeue. – Alexandru

+0

@AlexeiLevenkov Ho aggiornato la domanda. Inoltre, non sono sicuro che ci sia un equivoco qui, ma quando ho detto che sto cercando di sincronizzare le cartelle su due macchine, non ho mai detto che erano sulla stessa rete. In effetti, l'operazione di enumerazione di cui sto chiedendo verrà eseguita e quindi l'idea era di avere due servizi WCF per confrontare i file tra le due macchine. Sto solo cercando di rendere l'enumerazione delle cartelle il più veloce possibile. – Alexandru

risposta

1

uso LINQ e task paralleli

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

E come LINQ aumenterà le prestazioni? – mybirthname

+1

Buon punto ... usa Parallel.ForEach, ma prenderà gli enumerabili LINQ. Nel tuo caso stai popolando le tuple. Non ho bisogno di farlo. Ma aggiungere uno strumento di misurazione delle prestazioni è la migliore per provare entrambe le soluzioni. Infine, se hai davvero bisogno di scrivere velocemente l'applicazione in C++. –

+0

Proverò a vedere quando ho tempo, ma c'è un problema con questo metodo: se scegli Tutte le Directory nella tua ricerca e la struttura delle directory contiene un collegamento (come un collegamento simbolico ad un'altra cartella) che crea un ciclo, il l'operazione di ricerca entra in un ciclo infinito. – Alexandru

Problemi correlati