[.NET4] Потоки + практика применения
От: xobotik Россия  
Дата: 11.11.10 22:20
Оценка:
Здравствуйте.

Решил потренироваться в работе с потоками в .NET 4.0 на следующей задачи:

Реализовать программу поиска дубликатов файлов по локальным дискам на компьютере.

Алгоритм работы программы:

• Подсчет папок на выбранных дисках (время: очень быстро);
• Сканирование папок на наличие файлов (время: нормальное);
• Сортировка файлов (время: очень быстро);
• Проверка заголовков файлов (время: нормальное);
• Полная проверка;
• Вывод дубликатов файлов в графический компонент (время: нормальное).

Алгоритм составлен по реализованной программе, скриншоты которой вот по этой ссылке (размер = 461.17 Кб).

При реализации данной задачи возникло несколько вопросов:

1) Какой оптимальный по скорости алгоритм для поиска папок необходимо реализовать?

Я попробовал реализовать, вот что получилось (скорость работы конечно не устраивает):
        private string Path { get; set; }
        private string SearchPattern { get; set; }
        private string[] NonSearhDirectorys { get; set; }

        private const int DefaultCapacity = 10 * 100 * 1000;


        public Search(string path, string searchPattern, string[] nonSearhDirectorys = null)
        {
            Path = path;
            SearchPattern = searchPattern;
            NonSearhDirectorys = nonSearhDirectorys;
        }
        private string[] GetFiles(string path)
        {
            return Directory.GetFiles(path, SearchPattern);
        }
        private static string[] GetFolders(string path)
        {
            return Directory.GetDirectories(path);
        }
        public IEnumerable<string> GetFolders()
        {
            IList<string> buf = new List<string>(DefaultCapacity);
            Queue<string> dirs = new Queue<string>(DefaultCapacity);
            dirs.Enqueue(Path);

            string[] paths = null;
            try
            {
                paths = GetFolders(dirs.Dequeue());
                paths = paths.Except(NonSearhDirectorys).ToArray();
            }
            catch (UnauthorizedAccessException) { }
            catch (DirectoryNotFoundException) { }
            catch (PathTooLongException) { }
            catch (ArgumentNullException) { }

            if (paths != null && paths.Length > 0)
            {
                for (int i = 0; i < paths.Length; i++)
                {
                    dirs.Enqueue(paths[i]);
                    buf.Add(paths[i]);
                }
                /*
                Parallel.For(0, paths.Length, i =>
                {
                    dirs.Enqueue(paths[i]);
                    buf.Add(paths[i]);
                    currentDir = paths[i];
                });
                 */
            }

            while (dirs.Count > 0)
            {
                string dir = dirs.Dequeue();
                paths = null;
                try
                {
                    paths = GetFolders(dir);
                }
                catch (UnauthorizedAccessException) { }
                catch (DirectoryNotFoundException) { }
                catch (PathTooLongException) { }
                catch (ArgumentNullException) { }

                if (paths != null && paths.Length > 0)
                {
                    for (int i = 0; i < paths.Length; i++)
                    {
                        dirs.Enqueue(paths[i]);
                        buf.Add(paths[i]);
                    }
                    /*
                    Parallel.For(0, paths.Length, i =>
                    {
                        dirs.Enqueue(paths[i]);
                        buf.Add(paths[i]);
                    });
                     */
                }
            }
            buf.Add(Path);
            return buf.AsEnumerable();
        }

2) Какой оптимальный алгоритм реализовать для поиска файлов в найденных папках?

Как реализовал:
        public IEnumerable<File> GetFiles(IEnumerable<string> folders)
        {
            foreach (string folder in folders)
            {
                string[] paths = null;
                try
                {
                    paths = GetFiles(folder);
                }
                catch
                {
                }
                if (paths != null && paths.Length > 0)
                {
                    foreach (string path in paths)
                    {
                        yield return new File(path);
                    }
                }
            }
        }

Где File
public class File : 
        IEqualityComparer<File>,
        IComparable,
        IComparable<File>
    {
        public string Path { get; private set; }
        public string Name { get; private set; }
        public DateTime LastWriteTime { get; set; }
        public long Size { get; private set; }

        private static FileInfo _fileInfo;

        public File(string path, string name, DateTime lastWriteTime, long size)
        {
            Path = path;
            Name = name;
            LastWriteTime = lastWriteTime;
            Size = size;
        }
        public File(string path) : this(path, 
            File.GetName(path), File.GetLastWriteTime(path), File.GetSize(path))
        {
            
        }
        public int CompareTo(object obj)
        {
            return Size.CompareTo(obj as File);
        }
        public int CompareTo(File other)
        {
            return Size.CompareTo(other.Size);
        }
        public override bool Equals(object obj)
        {
            return obj is File && Equals((File) obj);
        }
        public bool Equals(File other)
        {
            if (ReferenceEquals(null, other)) return false;
            if (ReferenceEquals(this, other)) return true;
            return Equals(other.Name, Name) && 
                other.LastWriteTime.Equals(LastWriteTime) && 
                other.Size == Size;
        }
        public bool Equals(File x, File y)
        {
            return x.Equals(y);
        }
        public override int GetHashCode()
        {
            unchecked
            {
                int result = (Name != null ? Name.GetHashCode() : 0);
                result = (result*397) ^ LastWriteTime.GetHashCode();
                result = (result*397) ^ Size.GetHashCode();
                return result;
            }
        }
        public int GetHashCode(File obj)
        {
            return obj.GetHashCode();
        }
        public static string GetName(string path)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }
            if (path.Length >= 260)
            {
                return default(string);
            }
            _fileInfo = new FileInfo(path);
            return _fileInfo.Name;
        }
        public static long GetSize(string path)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }
            if (path.Length >= 260)
            {
                return default(long);
            }
            _fileInfo = new FileInfo(path);
            return _fileInfo.Length;
        }
        public static int GetSizeInMegaBytes(string path)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }
            if (path.Length >= 260)
            {
                return default(int);
            }
            _fileInfo = new FileInfo(path);
            long buf = _fileInfo.Length / 1024;
            return (int)buf / 1024;
        }
        public static DateTime GetLastWriteTime(string path)
        {
            if (String.IsNullOrEmpty(path))
            {
                throw new ArgumentNullException("path");
            }
            if (path.Length >= 260)
            {
                return default(DateTime);
            }
            _fileInfo = new FileInfo(path);
            return _fileInfo.LastWriteTime;
        }

3) При выполнении в созданном потоке (или в экземпляре Task),
если мы хотим отобразить в графический компонент какой-либо результат,
правильно ли пользоваться вот такой схемой?
if (ГрафическийКомпонент1.InvokeRequired || 
    ГрафическийКомпонент2.InvokeRequired || … ||
    ГрафическийКомпонентN.InvokeRequired)
{
    Action actionГрафическийКомпонент1 = () => … ;
    Action actionГрафическийКомпонент2 = () => … ;
    …
    Action actionГрафическийКомпонентN = () => … ;

    ГрафическийКомпонент1.Invoke(actionГрафическийКомпонент1);
    ГрафическийКомпонент2.Invoke(actionГрафическийКомпонент2);
    …
    ГрафическийКомпонентN.Invoke(actionГрафическийКомпонентN);
else
{
    вызов методом не через делегат Action                
}

4) Из алгоритма работы программы (1 пункт).
Как нам при работе в потоке метода подсчета папок возвращать их количество
в компонент label не привязывая метод подсчета папок к GUI (то есть разделенная логика)?

5) Такой же вопрос, только если нам необходимо возвращать множество данных
в графический компонент при работе одного метода в потоке и, не привязываясь к GUI?

6) Из алгоритма работы программы (4 пункт).
Как проверять заголовок файла и что это вообще такое?

7) Из алгоритма работы программы (последний пункт).
Как быстро вывести множество элементов (дублированных файлов) в графический компонент?
Может быть, использовать виртуальный DataGridView?

8) Возможно ли применение, каких – либо шаблонов проектирования в данной задачи? Если не сложно, скажите каких.

Заранее огромное спасибо!

P.S. По возможности буду сразу отвечать на новые сообщения в той теме и обязательно оценивать =)

Ссылка на текстовую версию темы (документ Word 97-2003)
С уважением!
поиск файлов поиск дубликатов файлов много поточный поиск
Re: [.NET4] Потоки + практика применения
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 11.11.10 23:52
Оценка: 2 (1) +1
Здравствуйте, xobotik, Вы писали:

На будущее — длинные простыни кода и пространные рассуждения очень маловероятно что кто то читать будет.
Теперь по делу:
1) Parallel в данном случае, имхо, слишком низкоуровневое и, как следствие, не очень удобное средство. Нужно использовать PLinq и фьючерсы (Task<T>).
2) Скачай и поковыряй VS Async CTP, все равно ведь баловством занимаешься.
3) Еще можно побаловаться с RX, но в фремворке точно нет асинхронного чтения списка файлов в директории, а есть ли оно на уровне WinAPI я ХЗ.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
AVK Blog
Re[2]: [.NET4] Потоки + практика применения
От: xobotik Россия  
Дата: 12.11.10 00:01
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, xobotik, Вы писали:


AVK>На будущее — длинные простыни кода и пространные рассуждения очень маловероятно что кто то читать будет.


Хммм =) я как бы еще сокращал и час целый работал над сообщением=) хотел просто все по делу и кратко =))

AVK>Теперь по делу:

AVK>1) Parallel в данном случае, имхо, слишком низкоуровневое и, как следствие, не очень удобное средство. Нужно использовать PLinq и фьючерсы (Task<T>).

На ус намотаю, только вот что там с остановками работы Task , продолжением и так далее, не разобрался.

AVK>2) Скачай и поковыряй VS Async CTP, все равно ведь баловством занимаешься.

AVK>3) Еще можно побаловаться с RX, но в фремворке точно нет асинхронного чтения списка файлов в директории, а есть ли оно на уровне WinAPI я ХЗ.

Попробую=) спасибо =)
С уважением!
Re[3]: [.NET4] Потоки + практика применения
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 12.11.10 00:10
Оценка:
Здравствуйте, xobotik, Вы писали:

AVK>>На будущее — длинные простыни кода и пространные рассуждения очень маловероятно что кто то читать будет.


X>Хммм =) я как бы еще сокращал и час целый работал над сообщением=) хотел просто все по делу и кратко =))


Да я все понимаю. Но обычно люди ценят свое время. Поэтому лучше дать краткий дайджест, а детали раскрыть ниже или вообще по требованию.

X>На ус намотаю, только вот что там с остановками работы Task , продолжением и так далее, не разобрался.


Ну вот заодно и разберешься. И не надо будет кучу очередей руками городить.
... << RSDN@Home 1.2.0 alpha 4 rev. 1476 on Windows 7 6.1.7600.0>>
AVK Blog
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.