#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);
HashSet files = 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();
Комментариев нет:
Отправить комментарий