Страницы

Поиск по вопросам

среда, 5 февраля 2020 г.

Оптимизировать работу с потоками

#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();

Комментариев нет:

Отправить комментарий