none
Получение Размера папки в C# RRS feed

  • Вопрос

  • вот так получаю размер папки 

    string[] fullfilesPath = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);
                long Size = 0;
                foreach (var item in fullfilesPath)
                {
                    FileInfo fi = new FileInfo(item);
                    Size += fi.Length;
                }

    Т. е. в переменной Size находится полный размер папки в байтах (но это не важно).

    Проблема в том что, когда этот код выполняется моя программа зависает. Есть только единственный способ решить эту проблему - выполнять этот код в другом потоке.

    Скажите пожалуйста как можно передать содержимое переменной Size в главный поток, и выполнить другое действие после передачи...

    По моему мне кажется что здесь надо использовать делегаты???

    1 сентября 2015 г. 14:22

Ответы

  • Пример использования BackgroundWorker.

    Кидаете его на форму и создаёте два обработчика события: DoWork и RunWorkerCompleted. В них пишете:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        var fileInfos = new DirectoryInfo(path)
                .EnumerateFiles("*.*", SearchOption.AllDirectories);
    
        foreach (var fi in fileInfos)
            size += fi.Length;
    }
    
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        labelSize.Text = size.ToString();
    }

    В данном случае path - это строковое поле с путём к нужному каталогу. size - тоже поле, потому что нужно использовать его значение в двух разных методах. labelSize - метка, куда выводится итоговое значение.

    Запускаем вычисления:

    backgroundWorker1.RunWorkerAsync();

    Вставьте эту строку в нужное место. Например, в код обработчика нажатия на кнопку (Button).

    Это всё! Вовсе несложно.

    • Предложено в качестве ответа Алексей ЛосевEditor 3 сентября 2015 г. 6:00
    • Помечено в качестве ответа motokraft 3 сентября 2015 г. 9:23
    2 сентября 2015 г. 18:36

Все ответы

  • Добрый день,

    а рекурсию не пытались использовать?

    Если без рекурсии(без вложенных папок), то так:

     foreach (var d in Directory.EnumerateDirectories(Path))
                {
                    long s = 0;
                    DirectoryInfo di = new DirectoryInfo(Path.Combine(Path,d));
                    FileInfo[] fiArr = di.GetFiles();
                    foreach (var f in fiArr)
                    {
                        s += f.Length;
                    }
                        Console.WriteLine("Dir: "+ d + " Size: "+s);
                    }
                }
    

    1 сентября 2015 г. 16:59
  • Добрый день.

    Можно запустить в BackgroundWorker. Посмотрите пример вот здесь. Там в конце есть пример.

    2 сентября 2015 г. 7:34
    Отвечающий
  • Добрый день.

    Можно запустить в BackgroundWorker. Посмотрите пример вот здесь. Там в конце есть пример.

    А по моему нет разницы как я выполняю этот код в другом потоке. Главное что бы в процессе выполнения кода можно было изменить текст в элементе label который находится в форме.

    Хотел использовать делегаты, но там чета так сложно!!!! Дайте пожалуйста ( или объясните ) как ими пользоваться????

    2 сентября 2015 г. 14:15
  • Вы бы описали подробней, что именно нужно, быстрей получили бы ответ. Что пробовали, что не получилось?

    Какую версию языка и фреймворка используете? Task и async доступны?

    Сперва вам нужно было передать значение long из другого потока, а теперь выясняется, что нужно ещё и Label на форме изменить. Label откуда: WindowsForms или WPF?

    2 сентября 2015 г. 17:54
  • Во-первых, вместо метода GetFiles лучше использовать метод EnumerateFiles. Первый пройдёт по всем папкам, создаст полный список файлов и вернёт его в виде массива. У вас, судя по тормозам, дерево файлов очень большое, значит массив много памяти будет занимать. А второй метод будет возвращать файлы по одному, не создавая их полного списка.

    Во-вторых, вместо статичесих методов класса Directory, возвращающих имена файлов в виде строк (string) есть смысл попробовать экземплярные методы класса DirectoryInfo - они возвращают сразу FileInfo. То есть не нужно будет дополнительно создавать экземпляры этого класса. В сочетании с EnumerateFiles это избавит от того, что ОС будет повторно обращаться к накопителю за информацией о длине файла, что устранит метания головок жёсткого диска туда-сюда.

    long size = 0;
    
    var fileInfos = new DirectoryInfo(path)
        .EnumerateFiles("*.*", SearchOption.AllDirectories);
    
    foreach (var fi in fileInfos)
        size += fi.Length;

    В-третьих, рассмотрите альтернативные способы. Например, этот.
    2 сентября 2015 г. 18:12
  • Пример использования BackgroundWorker.

    Кидаете его на форму и создаёте два обработчика события: DoWork и RunWorkerCompleted. В них пишете:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        var fileInfos = new DirectoryInfo(path)
                .EnumerateFiles("*.*", SearchOption.AllDirectories);
    
        foreach (var fi in fileInfos)
            size += fi.Length;
    }
    
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        labelSize.Text = size.ToString();
    }

    В данном случае path - это строковое поле с путём к нужному каталогу. size - тоже поле, потому что нужно использовать его значение в двух разных методах. labelSize - метка, куда выводится итоговое значение.

    Запускаем вычисления:

    backgroundWorker1.RunWorkerAsync();

    Вставьте эту строку в нужное место. Например, в код обработчика нажатия на кнопку (Button).

    Это всё! Вовсе несложно.

    • Предложено в качестве ответа Алексей ЛосевEditor 3 сентября 2015 г. 6:00
    • Помечено в качестве ответа motokraft 3 сентября 2015 г. 9:23
    2 сентября 2015 г. 18:36
  • Пример использования потока. Поток берём из пула.

    ThreadPool.QueueUserWorkItem(value =>
    {
        var fileInfos = new DirectoryInfo(path)
            .EnumerateFiles("*.*", SearchOption.AllDirectories);
    
        foreach (var fi in fileInfos)
            size += fi.Length;
    
        if (labelLen.InvokeRequired)
        {
            labelLen.Invoke(new MethodInvoker(() =>
            {
                labelSize.Text = size.ToString();
            }));
        }
        else
        {
            labelSize.Text = size.ToString();
        }
    });

    Вставьте этот код в нужное место (нажатие кнопкки).

    Тут используются те же поля path, size и labelSize.

    Вычисление суммы размеров всех файлов будет произведено в отдельном потоке. Потом нужно вывести это значение на метку. Это можно сделать только из того же потока, где был создан этот компонент! Поэтому приходится использовать метод InvokeRequired - он проверяет, нужен ли косвенный вызов. Если да - на ходу создаётся делегат MethodInvoker, предназначенный специально для этого случая. Если нет - устанавливаем значение метки напрямую.

    Часто пишут без условного оператора if. Просто сразу вызывают код через делегат:

    labelLen.Invoke(new MethodInvoker(() =>
    {
        labelSize.Text = size.ToString();
    }));

    Без if! Это упрощает код, но в итоге будет происходить косвенный вызов, даже когда он не нужен, что может снизить быстродействие.

    2 сентября 2015 г. 18:50
  • Пример использования задачи - Task.

    Task.Run<long>(() =>
    {
        var fileInfos = new DirectoryInfo(path)
            .EnumerateFiles("*.*", SearchOption.AllDirectories);
    
        foreach (var fi in fileInfos)
            size += fi.Length;
    
        return size;
    })
    .ContinueWith(task =>
    {
        long sizeResult = task.Result;
        labelSize.Text = sizeResult.ToString();
    
    }, TaskScheduler.FromCurrentSynchronizationContext());

    Вычисление суммы размеров файлов происходит в задаче, запускаемой методом Task.Run. Она возвращает значение типа long. Прямо в этой же задаче установить значение labelSize.Text нельзя по той же причине, что и раньше: это нельзя делать из другого потока.

    Задачи (Task) имеют очень удобную возможность - продолжения (continuation). В данном случае продолжение создаётся вызовом метода ContinueWith. Параметром служит задача, созданная и выполненная в методе Run. Получаем результат этой задачи и присваиваем его метке. Почему это возможно? Продолжению мы передали параметр TaskScheduler, который был создан из текущего контекста. А текущим контекстом на момент вызова задачи является поток формы. Вот в этот поток и будет передано выполнение. Поэтому в нём можно напрямую обращаться к элементам формы, без проверки InvokeRequired и вызова метода Invoke.

    2 сентября 2015 г. 19:02
  • Пример использования async/await.

    async void buttonSize_Click(object sender, System.EventArgs e)
    {
        var task = Task.Run<long>(() =>
        {
            long size = 0;
    
            var fileInfos = new DirectoryInfo(path)
                .EnumerateFiles("*.*", SearchOption.AllDirectories);
    
            foreach (var fi in fileInfos)
                size += fi.Length;
    
            return size;
        });
    
        await task;
    
        labelSize.Text = task.Result.ToString();
    }

    Во фреймворке нет асинхронного метода получения информации о файле, поэтому приходится вручную запускать задачу. Несложно заметить, что код тот же самый, что в прошлом примере. Только вместо продолжения мы используем ожидание - await. Обратите внимание, что в этом случае метод (обработчик события нажатия на кнопку) обязательно нужно пометить ключевым словом async.

    В результате продолжение создаст для нас компилятор. И всё, что после await, будет выполняться в этом продолжении. Контекст синхронизации по умолчанию будет использоваться тот же самый, в котором вызвана задача. Поэтому к элементам формы можно обращаться напрямую.

    Кстати, и в данном, и в предыдущем случае переменную size можно сделать локальной, а не полем класса.

    Напоследок отмечу, что асинхронность не предназначена для распараллеливания вычислений. И данный пример лишь с натяжкой годится.

    2 сентября 2015 г. 19:18
  • Спасибо большое! воспользовался этим решением моей проблемы!!!
    3 сентября 2015 г. 9:24