Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.77/13: Рейтинг темы: голосов - 13, средняя оценка - 4.77
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
1

Многопоточная обработка мелких файлов

24.06.2014, 21:42. Показов 2720. Ответов 29
Метки нет (Все метки)

Добрый день!

Программа через ThreadPool обрабатывает массив файлов:

C#
1
2
3
4
   foreach (string file in Files)
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(Check), (object)file);
                }
Метод Check() читает файл, делает различные проверки, в конце - пишет результаты в таблицу.

Я заметил, что если программа обрабатывает крупные файлы (средним объемом 50 Мб), то скорость работы получается максимально высокой. Если программа обрабатывает мелкие файлы (по 1 Мб), то скорость обработки падает в несколько раз.

Как считаете, можно ли как-то ускорить обработку мелких файлов, не меняя коренным образом программу?

Добавлено через 1 минуту
Я так понял, что основные затраты состоят в том, что:
- на каждый файл создается отдельный поток;
- в методе check() ведется некая подготовка для чтения файла
- вывод результатов в DataGridView происходит чаще, если если обрабатывать крупные файлы.

Добавлено через 38 минут
В Parallel.ForEach есть такая штука встроенная "Partitioner". Она позволяет не создавать отдельный поток для каждого файла, а создавать один поток, а потом направлять в него некую "порцию" файлов. Таким образом, более эффективно распределяя нагрузку. Я, вот, думаю, можно ли для ThreadPool что-то подобное сделать.

Добавлено через 7 минут
Думаю, как бы в качестве объекта в строку
ThreadPool.QueueUserWorkItem(new WaitCallback(Check), (object)file);

передавать не один (object)file, а, скажем массив, состоящий из нескольких файлов общим объемом не менее 50 Мб. Если же какой-то файл сразу больше 50 Мб, то передавать только один файл в массиве.
__________________
Помощь в написании контрольных, курсовых и дипломных работ здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.06.2014, 21:42
Ответы с готовыми решениями:

Многопоточная обработка
Здравствуйте! Есть while, который берет записи из базы данных и обрабатывает их с помощью методов...

Многопоточная обработка данных + ввод/вывод
Доброго времени суток! Пишу небольшое консольное приложение, и у меня возникли некоторые вопросы....

Скачивание множества мелких файлов
Почему webClient.DownloadFileAsync на скачивает файлы и программа переходит к следующему методу?...

Многопоточная обработка списка
Друзья, подскажите, пожалуйста, правильно ли я делаю. Задача: Я реализовал это следующим...

29
9 / 9 / 5
Регистрация: 23.06.2014
Сообщений: 40
24.06.2014, 21:44 2
Можно, есть штука под названием Task - она как раз не создает новый поток, а использует уже существующие.
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
24.06.2014, 21:47  [ТС] 3
Мне нужно на ThreadPool, т.к. c# 2.0 использую.

Разгадка, скорей всего в последнем абзаце сабжевого поста. То есть нужно делать файлы на массивы из "порций" по 50 Мб, а потом передавать в ThreadPool массивы.
0
9 / 9 / 5
Регистрация: 23.06.2014
Сообщений: 40
24.06.2014, 21:50 4
И что? передадите 50 файлов по 1 мегабайту, или по 5000 файлов по 10 кб, получите дохера потоков,
которые будут открывать (относительно медленная операция), работать с файлами, и закрывать их(тоже относительно медленная).
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
24.06.2014, 21:56  [ТС] 5
Нет. Если я не 50 раз передам по файлу в 1 Мб:
C#
1
 ThreadPool.QueueUserWorkItem(new WaitCallback(Check), (object)file);
а один раз передам массив, состоящий из 50 файлов
C#
1
 ThreadPool.QueueUserWorkItem(new WaitCallback(Check), (object)arrayOfFiles);
то, по идее, во втором случае один поток будет?
0
9 / 9 / 5
Регистрация: 23.06.2014
Сообщений: 40
24.06.2014, 22:01 6
Да, разумеется, отработает в одном потоке. Вызываете же один раз
ThreadPool.QueueUserWorkItem
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
24.06.2014, 22:03  [ТС] 7
Значит, я на верном пути, дон Карлос.
0
Master of Orion
Эксперт .NET
6087 / 4943 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
25.06.2014, 10:03 8
Suppir, тред пул не создает по потоку на задачу, на то он и тредпул. Поэтому 4 файла по 10 кб или 4 файла по 100мб, будет работать в одно и то же число потоков. Скорее всего, падение скорости связанно со случайным доступом к данным. Вместо того, чтобы гадать, нужно запускать профайлер и смотреть.
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
25.06.2014, 10:21  [ТС] 9
Я для теста проверяю 17 тыс. мелких файлов общим объемом 200 Мб.

Был такой код посылания в ThreadPool:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
  foreach (string file in Files)
                {
                    ThreadPool.QueueUserWorkItem(new WaitCallback(Check), (object)file);
                    Thread.Sleep(10);
                }
 
                  lock (WorkerLocker)
                {
                    while (RunningWorkers > 0)
                    {
                        Monitor.Wait(WorkerLocker);
                    }
                }
Время работы - аж 180 секунд (очень медленно).

Сейчас переписал на такой код:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   StringBuilder sb = new StringBuilder();
    RunningWorkers = 0;
    int allSize = 0;
    foreach (string file in Files)
    { 
        FileInfo fi = new FileInfo(file);
        int size = Convert.ToInt32(fi.Length);
        allSize += size;
 
        sb.Append("|");
        sb.Append(file);
 
        if (allSize > 1000000)
        {
            allSize = 0;
            RunningWorkers++;
            ThreadPool.QueueUserWorkItem(new WaitCallback(Check), (object)sb);
            Thread.Sleep(10);
            sb = new StringBuilder();
        }
    }
    if (sb.ToString().Length > 0)
    {
        RunningWorkers++;
        ThreadPool.QueueUserWorkItem(new WaitCallback(RunCheck), (object)sb);
        Thread.Sleep(10);
    }
Время работы - 7 секунд! То есть формирую список файлов из 10 Мб, потом посылаю на функцию.

Не знаю почему, но список через файлом List<string> не получается формировать. Передается всего один файл. Поэтому формирую StringBuilder с названиями файлов, а внутри функции разделяю этот StringBuilder с помощью Split на названия файлов.

Добавлено через 44 секунды
Пока что код косячный, есть разные глюки, но направление, скорее всего, правильное.

Добавлено через 5 минут
Возможно, еще связано с тем, что перед каждой обработкой файла инициируются многие переменные, списки, регулярные выражения - это также отнимает время. Сейчас я делаю эту инициацию не перед каждым из 17000 файлов, а перед 20 "порциями" файлов по 10 Мб. То есть почти в тысячу раз реже. Инициация занимает 5 миллисекунд по тестам. Умножаем на 17 тыс. = 85 секунд простоя.
0
987 / 885 / 354
Регистрация: 24.03.2014
Сообщений: 2,381
Записей в блоге: 2
25.06.2014, 10:27 10
Если там нигде реально долгоиграющих действий нет, то в основном потери будут из-за железа, как и сказал Psilon, из-за случайного доступа к данным. То, что Вы делаете, в данном конкретном случае отработало 7 секунд, при других файлах, размерах, дефрагментированности диска, отработает совсем иначе.
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
25.06.2014, 10:38  [ТС] 11
Запускаю на SSD-винчестере. Не знаю, влияет ли там дефрагментация.

Добавлено через 1 минуту
Я не могу только понять, почему не удается формировать List<string> с названиями файлов. Если передаю в ThreadPool, а потом обнуляю (ведь нужно дальше заново формировать), то внутри ThreadPool при перечислении файлов пишет, что список был изменен, перечисление невозможно.

Как думаете, можно ли это исправить?
0
Master of Orion
Эксперт .NET
6087 / 4943 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
25.06.2014, 10:38 12
Suppir, тогда странно.

Да и если не себе программа пишется, лучше протестировать на компьютерах с обычным hdd. Потому тут уже в обратную сторону будет работать

Любая программа может работать быстро. Если работает медленно, значит что-то делается не так. Трудно так сходу сказать, что конкретно, но вот наличие этого тупика очевидно.
0
987 / 885 / 354
Регистрация: 24.03.2014
Сообщений: 2,381
Записей в блоге: 2
25.06.2014, 10:45 13
Цитата Сообщение от Suppir Посмотреть сообщение
то внутри ThreadPool при перечислении файлов пишет, что список был изменен, перечисление невозможно
Значит где-то модифицируете коллекцию...
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
25.06.2014, 10:46  [ТС] 14
Может быть еще влияет, что стоит:
C#
1
2
ThreadPool.QueueUserWorkItem(new WaitCallback(Check), (object)sb);
            Thread.Sleep(10);
То есть после каждой отправки на тредпул идет пауза. Если умножить на 17 тыс. файлов, получается внушительное время.
0
987 / 885 / 354
Регистрация: 24.03.2014
Сообщений: 2,381
Записей в блоге: 2
25.06.2014, 10:49 15
Ну да, внушительное... а зачем собственно передышка нужна? GUI?
И что вообще с такой кучей файлов делается?
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
25.06.2014, 10:53  [ТС] 16
Spawn,

Я пробовал передавать List<string> из файлов следующим образом:

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
                List<string> Partition = new List<string>();
 
                int allSize = 0;
                foreach (string file in Files)
                { 
                    FileInfo fi = new FileInfo(file);
                    allSize += Convert.ToInt32(fi.Length);
 
                    Partition.Add(file);
 
                    if (allSize > 1000000)
                    {
                        allSize = 0;
                        RunningWorkers++;
                        ThreadPool.QueueUserWorkItem(new WaitCallback(RunCheck), (object)Partition);
                        Thread.Sleep(10);
                        Partition.Clear();
                    }
                }
                if (Partition.Count > 0)
                {
                    RunningWorkers++;
                    ThreadPool.QueueUserWorkItem(new WaitCallback(RunCheck), (object)Partition);
                    Thread.Sleep(10);
                }
 
                lock (WorkerLocker)
                {
                    while (RunningWorkers > 0)
                    {
                        Monitor.Wait(WorkerLocker);
                    }
                }

Внутри RunCheck я просто перебираю список файлов. Но там получается, что этот список обнуляется извне. Скорее всего, кодом Patriotion.Clear(), который идет после ThreadPool.QueueUserWorkItem.

Как думаете, можно ли это как-то исправить?

Добавлено через 2 минуты
Цитата Сообщение от Spawn Посмотреть сообщение
Ну да, внушительное... а зачем собственно передышка нужна? GUI?
И что вообще с такой кучей файлов делается?
Отправляется файл на парсер. Затем он проверяется разными сложными функциями, ищутся ошибки, формируется результат в DataTable. В конце обработки файла результат (если есть) пишется в GUI. Также обновляется информация в статусбаре. И еще пауза нужна, чтобы пользователь мог останавливать процесс. Если без паузы отправить сразу все файлы на Threadpool, тогда придется ждать завершения всей обработки.
0
Master of Orion
Эксперт .NET
6087 / 4943 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
25.06.2014, 10:58 17
Suppir, пауза в коде - ошибка в коде. Простое правило
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
25.06.2014, 11:04  [ТС] 18
Psylon, вы знаете какой-нибудь более правильный вариант для ThreadPool?

Я использую паузу после ThreadPool, потом:

C#
1
2
3
4
5
6
7
    lock (WorkerLocker)
                {
                    while (RunningWorkers > 0)
                    {
                        Monitor.Wait(WorkerLocker);
                    }
                }
А внутри ThreadPool:

C#
1
2
3
4
5
6
 //Ждем завершение треда
            lock (WorkerLocker)
            {
                RunningWorkers--;
                Monitor.Pulse(WorkerLocker);
            }
0
1 / 1 / 0
Регистрация: 20.06.2014
Сообщений: 17
25.06.2014, 13:16 19
Цитата Сообщение от Suppir Посмотреть сообщение
Внутри RunCheck я просто перебираю список файлов. Но там получается, что этот список обнуляется извне. Скорее всего, кодом Patriotion.Clear(), который идет после ThreadPool.QueueUserWorkItem.
Как думаете, можно ли это как-то исправить?
Можно внутри метода RunCheck делать копию списка Patriotion и перебирать копию. Или сразу передавать копию.
0
25 / 25 / 10
Регистрация: 08.08.2011
Сообщений: 1,160
25.06.2014, 13:22  [ТС] 20
Я пробовал создать копию внутри RunCheck - примерно то же самое получается.

А как передавать копию? В смысле, присвоить значения другому списку, передать его? Но ведь другой список также придется обнулять. И если он будет в локальной видимости:

C#
1
2
3
4
5
6
7
8
9
if (allSize > 1000000)
                    {
                        allSize = 0;
                        RunningWorkers++;
                        List<string> Copy = new List<string> (Partition) 
                        ThreadPool.QueueUserWorkItem(new WaitCallback(RunCheck), (object)Copy);
                        Thread.Sleep(10);
                        Partition.Clear();
                    }
И если этот список Copy будет в локальной видимости, то он сам уничтожится после передачи в ThreadPool. Проблема, как я понял, в том, что ThreadPool отрабатывает моментально, а сразу за ним идет обнуление списка с файлами.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
25.06.2014, 13:22

Многопоточная обработка массива
В общем, есть два задания: 1. Реализуйте последовательную обработку элементов вектора, например,...

Многопоточная обработка структур
Прошу сильно не ругать. Нужна помощь со студенческим заданием. Задание такое: В работе необходимо...

Многопоточная обработка файла
Всем доброго времени суток! У меня есть задача параллельной обработки файла некоторым...

c# многопоточная обработка транзакций
Мне нужно максимально быстро обработать некоторое кол-во транзакций (от 1 до нескольких тысяч)....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.