2014-10-12 23 views
6

Farklı makinelerdeki dosyaları iki klasör arasında senkronize etmek için bir dosya eşitleme hizmeti üzerinde çalışıyorum. son yazma zamanı içeren bu dizin içindeki tüm dosya yolları ve alt dizin yollarınınDizin verilerini .NET'te almak için en hızlı yol.

  • bir veri yapısını veya yapıları,: Ben bir dizin numaralandırmak ve ondan sonraki bilgi çekmesi çok hızlı bir yol bulmalıyız her dosya veya alt dizin için.
  • Geçerli dizinin altındaki herhangi bir düzeyde bulunan her bir alt dizin için yukarıdakiyle aynıdır.

Şimdiye kadar, bu ile geldi:

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

Performans açısından bu (bakılmaksızın ekli debugger olmaksızın serbest bırakılması ya da ayıklama modunda çalışan) yaklaşık 22 saniye sürer numaralandırmak benim C: \ drive ve erişimi olan yaklaşık 549,254 dosya ve 83,235 klasörlerin bir listesini üretmek, ancak daha hızlı olabilir? MSVC++ önerileri bile herhangi bir öneriye açığım.

Düzenleme: Çoklu bastırma nedeniyle LINQ's AsParallel ile 12 saniye (Release modunda test edilmelidir). Bunun tüm C: \ alt klasörleri için paralel olduğunu, ancak yukarıda anlattığım tek iş parçacıklı uygulama için yinelemeli çağrılar yapılacağını unutmayın, aksi takdirde tüm klasörleri her zaman paralel hale getirmek GERÇEKTEN uzun bir zaman alır!

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

Düzenleme: Mark Gravell dan Alexei en bağlantılı çözümün kabul cevabı kullanarak Hala yaklaşık 21 saniye.

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

Düzenleme: Bu özyinesiz tekniktir (iterek ve yığın bu yönteme aramaları haşhaş maliyeti kadar pahalı bu Kuyruk veri türü canlı tutma muhtemelen maliyeti olan) en hızlı değildir: Bu soru, .NET'e doğru açık uçludur, çünkü MSVC++ kütüphaneleriyle daha hızlı bir şekilde daha hızlı bir yol olabilir, ancak daha hızlı bir yönteme rastlamadım. Eğer C++ metoduyla C/C metodum daha hızlı bir C sürücüsü sayımcısı ile aynı veriyi çekebilirse, her şeyden önce bunu yapmak için her şeyden önce kudos, ikincisi onu gerçekten görmek isterdim. bir çok insan dışarıda (sadece kendimde değil). Aşağıdaki yöntem etrafında 200.000 ms aldı fark kadar çok daha uzun herhangi bir kodu daha çok uzak boost içine bu var yukarıda gönderdiniz:

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

Düzenleme: FindFirstFile ve FindNextFile için Pınvoke kullanma yaklaşık 6 aldı Tüm C sürücümü tekrarlamak için saniyeler (yinelenen bağlantı ve Sam Saffron'un cevabı sayesinde). Ama ... daha hızlı olabilir?

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

Düzenleme: Evet, daha hızlı olabilir. Hedef klasörün alt dizin yinelemelerini paralel hale getirmek için teknikler kullanarak, yukarıdaki FindNextFilePInvokeRecursive yöntemini kullanarak 4 saniyeye ulaşabilirim. Tüm C sürücüsünü ihtiyacım olan verilerle yinelemek için 4 saniye. Süreç monitöründe görebiliyorum, yaklaşık% 30 CPU ve en fazla% 1 disk yiyorum, ki bu da bana biraz garip geliyor, neden şu an olduğundan emin değil, belki sadece bu bağlantılı liste geçiş stili oldukça ihmal edilebilir.İdeal olarak, en azından% 100 CPU'yu tüketmelidir, ancak bu, paralelleştirdiğiniz alt klasörlerin sayısına ve derinliğine bağlı olabilir. Ama daha hızlı olabilir mi ?!

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

Düzenleme: paralellikler kullanırken aksi takdirde bir istisna yakalayabilirsiniz, eşzamanlılık kilitleri eklemek unuttum. Ayrıca tuples kaldırıldı ve benim amaçlar için bir FileInformation/DirectoryInformation sınıf ile gitti. Bu traş edildi .5 saniye. Şimdi C: sürücümü numaralandırmak için 3.5 saniye.

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

Düzenleme: B.K. FILETIME Tarih saat dönüşümle ilgili soruyordu:

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 ben, sonra LINQ en çoklu kullanarak neredeyse iki kat daha hızlı olması için hızlarını artırabilir eğer düşünüyorum İyileştirmek için daha fazla alan olabilir ve bu sorunun insanlığın iyiliği için yeniden açılmasını istiyorum. :) – Alexandru

+0

@AlexeiLevenkov Yinelenen bir dosya olduğunu sanmıyorum çünkü çoğaltma türü yerine son yazma süresi için sorgulamam gerekiyor. Örneğin bağladığınız soruda Mark Gravell'in kabul edilen çözümünü kullanın, ihtiyacım olan tüm verileri değil, dizeleri döndüren Directory.GetFiles veya Directory.GetDirectories'i kullanır ve eğer bunu kullanmazsanız, özyinelemeli olmayan bir yaklaşım kullanmak için değiştirirseniz .GetFiles/Direct, muhtemelen bekleme sırasındaki yaklaşık 22 saniye sürüyor (yine benim ilk, yinelemeli uygulamamla aynı), çünkü kuyruk sırasını aktif tutmak ve enqueuing/dequeueing maliyetini korumak zorunda kalıyor. – Alexandru

+0

@AlexeiLevenkov Soruyu güncelledim. Ayrıca, burada bir yanlış anlaşılma olup olmadığından emin değilim, ancak iki makinede klasörleri senkronize etmeye çalıştığımı söylediğimde, aynı ağda olduklarını söylemedim. Aslında, sorduğum numaralandırma işlemi yapılacak ve daha sonra iki makine arasındaki dosyaları iki WCF servisiyle karşılaştırmaktı. Sadece klasör numaralandırma işlemini olabildiğince hızlı yapmaya çalışıyorum. – Alexandru

cevap

1

kullanım LINQ ve Paralel Görevler

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

LINQ performansı nasıl artıracak? – mybirthname

+1

İyi nokta ... Parallel.ForEach kullanın, ancak LINQ numaralandırmaları alacaktır. Sizin durumunuzda, Tuples'i dolduruyorsunuz. Bunu yapmana gerek yok. Ancak bir performans ölçüm aracı eklemek, ya çözümü kanıtlamak için en iyisidir. Son olarak, gerçekten hızlı bir şekilde ihtiyacınız varsa, uygulamayı C++ 'ya yazınız. –

+0

Zamanım olduğunda ne zaman çalıştığımı göreceğim ama bu yöntemle ilgili bir sorun var: Aramanızdaki AllDirectories'i seçerseniz ve dizin yapısı bir döngü oluşturan bir bağlantı (başka bir klasöre sembolik bir bağlantı gibi) içeriyorsa, arama işlemi sonsuz bir döngüye girer. – Alexandru

İlgili konular