LINUX.ORG.RU

[C/C++]Обход директории. Многопоточность.


0

2

Парни я вот чо-то не могу въехать. Мне например нужно находясь в текущей папки пройтись по ней и по всем дочерним папкам, использую несколько потоков. Как?! У меня пока ничего хорошего не получилось. Кто, что, может посоветует?!

(=

Это основное тестовое задание для младшего разработчика компании eSignal =)

Написать нужно либо в MFC, либо в Qt ( но так-как HR-менеджер указывает на портируемость, то писать приходится на Qt =) ).

=)

Вспомнилось просто =)

m4n71k0r
()

у меня пока ничего хорошего не получилось.

не важно хорошо или не хорошо, расскажите каким путем вы пошли

anonymous2
()
Ответ на: комментарий от rival

Если память не изменяет, то нужно было написать, чтобы программа не подавала признаков зависания при выборе директории и отображении её содержимого (поддиректорий и файлов), также необходимо было выводить статистику по расширениям файлов (всем файлам и в частности по файлам в текущей директории без учёта вложенных директорий). И всё это должно быть реализовано минимумом кода с максимумом «платформонезависимости».

Как-то так. Прямого ограничения на использование возможностей Qt не было.

m4n71k0r
()
Ответ на: (= от m4n71k0r

Так это, создаешь на основе QThread поток, который обходит директории и выдаёт промежуточные результаты через сигналы в гуй, не?

Kosyak
()
Ответ на: комментарий от Kosyak

А без потока? А то повадились, чуть что, сразу поток.

baverman
()
Ответ на: комментарий от Legioner

Действительно, какой смысл? Упирается в IO. Я бы сделал обход и получение файлов однопоточным, а саму обработку, только если она того стоит, можно сделать многопоточной. Например, через акторы, посылая им задания асинхронно. Такое поведение при желании можно имитировать доступными средствами на более простых языках.

dave
()

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

С самоделками можно нарватся либо на срыв стека, либо на исчерпание числа макс дескрипторов, если файловая система будет оооочень глубокой (тысячи вложенных каталогов),

hydraulicbrakefluid
()
Ответ на: комментарий от hydraulicbrakefluid

>глубокой (тысячи вложенных каталогов),

ИМХО тысячные уровни вложенности загнут любого. А кто-нибудь видел такую ситуацию. Не, я понимаю есть тысячи объектов в директории, но чтобы тысячи уровней вложенности.

pathfinder
()
Ответ на: комментарий от pathfinder

> ИМХО тысячные уровни вложенности загнут любого. А кто-нибудь видел такую ситуацию. Не, я понимаю есть тысячи объектов в директории, но чтобы тысячи уровней вложенности.

Не загнут, если через продолжения писать. Заменить обычную рекурсию на хвостовую. В F# для этого достаточно просто весь код обернуть в монаду Async и, вуаля, будет готово. Но задача, да, выдуманная :)

dave
()
Ответ на: комментарий от dave

Да, вообще, можно и без акторов обойтись. Есть простая очередь заданий, защищенная блокировкой. Есть пул исполняющих потоков. Пришло новое задание - ставим в очередь и будим потоки. Один из потоков забирает задание и исполняет. Все просто.

dave
()
Ответ на: комментарий от baverman

Нет, конечно. Только монада Async - это самое простое в использовании из того, что мне известно. Там пишется как бы «обычный» код и оборачивается в тег «async { ... }». Затем компилятором такой код автоматически разворачивается в хвостовую рекурсию. Минимум ручной работы :)

dave
()
Ответ на: комментарий от dave

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

baverman
()
Ответ на: комментарий от baverman

>по-моему обход дерева каталогов так просто не развернешь.

Я тоже так считаю. Как минимум одну «переменную»/«уровень вложенности» надо иметь. Другое дело, если итеративный процесс будет более экономично расходовать память. Это возможно, но принципиально проблему сверхвысокого уровня вложенности решить не удастся.

pathfinder
()

нужно каждую папку сканировать отдельным потоком или просто сканирование сделать в потоке отличном от потока в котором крутится GUI messages?

ACR
()
Ответ на: комментарий от pathfinder

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

pathfinder
()
Ответ на: комментарий от ACR

>нужно каждую папку сканировать отдельным потоком

А если этих папок ОЧЕНЬ много, то понадобится ОЧЕНЬ много потоков.

pathfinder
()
Ответ на: комментарий от ACR

>или просто сканирование сделать в потоке отличном от потока в котором крутится GUI messages?

А вот это уже здравая мысль. Я думаю таким решением можно и ограничиться.

pathfinder
()
Ответ на: комментарий от baverman

Разворачивается. Async не накладывает ограничений. Просто нужно обернуть вычисление в монаду. Тогда все while/for/try преобразуются в функциональную форму с гарантированным TCO. Это же касается рекурсивных вызовов, которые теперь превращаются в применение монадического bind - для него TCO тоже гарантировано. Тем более, что while строится через bind, а for - через while и try.

Это в ответ на ложное на мой взгляд утверждение, что «тысячные уровни вложенности загнут любого». Привел пример, где это не так.

dave
()
Ответ на: комментарий от Bad_Habit

> На Qt такая задачка решается даже без фантазии.

ага, щаззз, на Qt такая задачка решается далеко не оптимальным образом

QDirIterator напрямую не дает информацию, на каталог он указывает или на файл, в результате, так как ТС нужен многопоточный обход, ему потребуется дополнительный вызов stat (например через QFileInfo) на каждый пункт каталога, а это может очень-очень сильно сказаться на скорости

dt1
()
Ответ на: комментарий от pathfinder

> А если этих папок ОЧЕНЬ много, то понадобится ОЧЕНЬ много потоков

ОЧЕНЬ много потоков для современных систем не экзотика.

ACR
()

Вместо рекурсии моделируешь стек на куче, ну и потом уже разбиваешь проход по каждой ноде из корневой директории на свой поток, например.

yoghurt
()
Ответ на: комментарий от yoghurt

Гм. Хотя рекурсивные вызовы для прохода по каждой ноде из корневой директории тоже можно написать..

yoghurt
()
Ответ на: комментарий от ACR

8 мб на стек для потока по умолчанию в современной системе линукс

dt1
()
Ответ на: комментарий от dt1

> QDirIterator напрямую не дает информацию, на каталог он указывает или на файл, в результате, так как ТС нужен многопоточный обход, ему потребуется дополнительный вызов stat (например через QFileInfo) на каждый пункт каталога, а это может очень-очень сильно сказаться на скорости

Есть QDir::entryInfoList()

Bad_Habit
()
Ответ на: комментарий от hydraulicbrakefluid

>файловая система будет оооочень глубокой (тысячи вложенных каталогов)

В природе существует ограничение на максимальную длину пути, если ты не знал. Называется PATH_MAX и срезает «тысячи вложенных каталогов» буквально до пары тысяч при условии, что название каждогокаталога состоит из одного символа.

linuxfan
()
Ответ на: комментарий от Bad_Habit

QDir использует его на внутреннем уровне. Ну и как я написал уже, вызов QFileInfo - это неминуемые тормоза.

Сейчас посмотрел исходный код QDirIterator, он сам QFileInfo использует для всего подряд, это просто жесть какая-то.

В общем, самый быстрый способ чтения каталога - это readdir.

dt1
()
Ответ на: комментарий от dave

>Это в ответ на ложное на мой взгляд утверждение, что «тысячные уровни вложенности загнут любого»

Я не специалист по функциональному программирование, но я по прежнему не верю, что монады что-то изменят. Ну не будет стека, будет какая-то другая структура данных, которая будет расти с ростом дерева директорий.

Может популярно объяснишь, за счет чего можно достичь константного потребления памяти при обходе неупорядоченного дерева директорий произвольного размера?

pathfinder
()
Ответ на: комментарий от dt1

> QDir использует его на внутреннем уровне.

Вот лажа, сам ни за что б не подумал. Qt провоцирует лень заглядывания в собственные сорцы..

В общем, самый быстрый способ чтения каталога - это readdir.

Ну так POSIX вообще лучший :)

Bad_Habit
()
Ответ на: комментарий от Legioner

просто рекурсивный обход есть, но хотелось бы использовать скажем 3-4 потока, но вот как сделать сихронизация между ними не пойму :(

Boy_from_Jungle
() автор топика
Ответ на: комментарий от Boy_from_Jungle

>просто рекурсивный обход есть, но хотелось бы использовать скажем 3-4 потока, но вот как сделать сихронизация между ними не пойму :(

А нельзя ли сделать что-то вроде Jobs и пару рабочих тредов. Каждый Job сканирует дерево до определенной глубины и, если не если не дошел до конца, то добавляет новые задания в очередь. Правда тут надо продумать детали. Очень легко вывалится в огромное количество заданий в очереди. Можно в задании указывать: сканировать элементы в данной директории «от сих до сих» и соответственно каждая директория может породить не более X заданий, вне зависимости от числа элементов в ней. Как-то так.

pathfinder
()
Ответ на: комментарий от Boy_from_Jungle

Ну и вообще тут говорили, что производительность упрется не в процессор а в дисковую подсистему.

pathfinder
()
Ответ на: комментарий от placement_new

а gcc ее кстати же поддерживает? хвостовую то.

Хвостовую - да, упрощает.

quasimoto
()
Ответ на: комментарий от pathfinder

fts_open - прекрасно справляется. Как-то приходилось писать AV-сканнер (на базе готового AV Engine), который должен быть устойчив к любому содержимому файлопомойки, так что не по наслышке знаю.

Для самоделки рекурсивной достаточно 1-2 тысяч вложенных каталогов, что-бы исчерпать лимит дескрипторов, если на каждом уровне держать открытый хендл (DIR*) директории, и вполне можно нарваться на срыв стека, если на каждом из уровней - перечитывать все содержимое каталога, так что тут нужно что-то очень гармоничное-продуманное, иначе — no way, fts_open наше все.

hydraulicbrakefluid
()
Ответ на: комментарий от Boy_from_Jungle

(=

Я просто попытался угадать, может это задание какое-то =)

Не-не-не вообще не троллю =)

Разве что оффтопики генерирую =)

m4n71k0r
()
Ответ на: комментарий от pathfinder

>Может популярно объяснишь, за счет чего можно достичь константного потребления памяти при обходе неупорядоченного дерева директорий произвольного размера?

очевидно, за счет изменения представоения дерева. например, в виде массива(списка)

yaws
()
Ответ на: комментарий от Boy_from_Jungle

Перевод на С++ и останов всего этого дела оставляю тебе

package org.absurd.mtfswalker;

import java.io.File;
import java.util.LinkedList;

public class Startup {
	static class Queue {
		private LinkedList queue = new LinkedList();
		
		synchronized void put(File file) {
			queue.addLast(file);
			notifyAll();
		}
		
		synchronized File get() throws InterruptedException {
			while (queue.isEmpty()) {
				wait();
			}
			return (File) queue.removeFirst();
		}
	}
	
	static final Queue QUEUE = new Queue();
	
	static class Worker implements Runnable {
		public void run() {
			try {
				while (true) {
					File f = QUEUE.get();
					if (f.isDirectory()) {
						File[] fileList = f.listFiles();
						for (int i = 0; i < fileList.length; i++) {
							if (fileList[i].isDirectory()) {
								QUEUE.put(fileList[i]);
							} else {
								System.out.println("Worker : " + Thread.currentThread().getName() 
										+ " visiting file " + fileList[i].getName());
							}
						}
					}
				}
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}
	}
	
	static final int WORKER_COUNT = 4;
	
	public static void main(String[] arg) {
		for (int i = 0; i != WORKER_COUNT; ++i) {
			new Thread(new Worker(), "WORKER_" + i).start();
		}
		QUEUE.put(new File("."));
	}
}
Absurd
()
Ответ на: комментарий от Bad_Habit

>На Qt такая задачка решается даже без фантазии.

Вот за это и не люблю Qt. Был графический тулкит, а в него понатаскали все, что только возможно.

Напрямую противоречит Unix way: библиотека выполняет одну функцию, но делает это хорошо.

ttnl
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.