#c_sharp
Господа, прошу помочь начинающему программисту. Только учусь работать с потоками, ну и для закрепления материала решил написать программу для поиска дубликатов файлов. Алгоритм выглядит примерно так: В отдельном потоке запускается функция которая ищет все файлы по указанному пути (в подпапках тоже), эти файлы записываются в HashSet; В процессе поиска этих файлов, в label выводится текущая папка, где программа ищет файлы; После окончания поиска, при помощи linq в этом массиве ищутся дубликаты файлов; Эти дубликаты выводятся в listView через foreach, при этом высчитывается процент выполнения работы и он отображается в ProgressBar'e. Всё бы ничего, да вот только удумал я замерить время выполнения поиска. Получилось порядка 1,05 минут. Ради эксперимента, взял и убрал вывод в label текущей папки, где идёт поиск. Вышло уже 44секунды. После убрал вывод в прогрессбар процента поиска, вышло (4 секунды!!!). Замеры производил при помощи StopWatch'a. Код представлен ниже: class FileClass { public string Name { get; set; } public double Size { get; set; } public string FullPath { get; set; } } public partial class Form1 : Form { delegate void PositionDelegate(int pos); HashSetfiles = new HashSet (); event PositionDelegate ChangePosition; int current; /// /// текущий прогресс работы /// int CurrentPosition { get { return current; } set { if (ChangePosition != null) ChangePosition(current); current = value; } } public Form1() { InitializeComponent(); ChangePosition += delegate(int pos) { if (progressBar1.InvokeRequired) progressBar1.Invoke(new MethodInvoker(delegate { progressBar1.Value = pos; })); else progressBar1.Value = pos; }; } ////// Отображение в label текущей папки /// /// void controlCallBack(string currentDir) { label1.Invoke(new MethodInvoker(delegate { label1.Text = currentDir; })); } ////// Поиск файлов в папках(включая подпапки) /// /// /// void getallfiles(DirectoryInfo dr, string pattern) { try { dr.GetFiles(pattern).ToList().ForEach(s => { files.Add(new FileClass() { Name = s.Name, FullPath = s.FullName, Size = s.Length }); controlCallBack(s.FullName); }); } catch (UnauthorizedAccessException) { } try { foreach (DirectoryInfo d in dr.GetDirectories()) getallfiles(d, pattern); } catch (UnauthorizedAccessException) { } } ////// создание потока для поиска дубликатов /// /// /// private void button1_Click(object sender, EventArgs e) { new Thread(new ThreadStart(delegate { getallfiles(new DirectoryInfo(@"C:\Users\lionell"), "*.*"); var dublicates = from rep in files group rep by rep.Name into g where g.Skip(1).Any() from rep in g select rep; double curr=0; listView1.Invoke(new MethodInvoker(delegate { listView1.BeginUpdate(); })); foreach (var f in dublicates) { listView1.Invoke(new MethodInvoker(delegate { ListViewItem it = new ListViewItem(f.Name); it.SubItems.Add(f.FullPath); listView1.Items.Add(it); })); curr += 1; CurrentPosition = int.Parse(Math.Round((curr / dublicates.Count()) * 100, 0).ToString()); } listView1.Invoke(new MethodInvoker(delegate { listView1.EndUpdate(); })); })) { IsBackground = true }.Start(); } Буду благодарен любой помощи
Ответы
Ответ 1
Вы делаете вывод в интерфейс через синхронные методы. Поэтому почти все преимущества параллельной обработки исчезают - ведь вызовы становятся в очередь, чтобы вывести значение в интерфейс. Как решение - отписывать информацию, которую требуется выводить в GUI в отдельном объекте, и генерировать асинхронные уведомление о том, что что-то изменилось. ГУИ будет обрабатывать эти уведомления и отображать изменения.Ответ 2
Создай таймер, который будет отображать текущее состояние на форме. И по таймеру читай это состояние из некоторого объекта, обновляемого в другом потоке.Ответ 3
Попробуйте вместо Invoke вызывать BeginInvoke. Отличие будет в том, что фоновый поток не будет ждать, когда пользовательский интерфейс обработает операцию, а немедленно получит экземпляр асинхронной операции, с которым можно что-нибудь сделать.Ответ 4
В вашем коде переменная dublicates имеет тип Enumerable. Это значит, вычисления с ней происходят лениво. То есть каждый вызов метода dublicates.Count() будет приводить к проходу по всей коллекции files. Чтобы устранить это, напишите: var dublicates = (from rep in files group rep by rep.Name into g where g.Skip(1).Any() from rep in g select rep) .ToArray();
Комментариев нет:
Отправить комментарий