Шаг 328 - Сканирование каталогов

Дата 23.02.01 11:34  
От кого Vlad Mogilevsky <vladosha@inbox.ru>
Кому <kaev@yandex.ru>

Задача программы была получив имя каталога, поискать в нем все каталоги. Получив список этих каталогов войти внутрь каждого и выбрать оттуда все файлы с определенным расширением. Тупой перебор рекурсивной функцией в один поток дает время выполнения около 2 часов при 200 тыс каталогов. Исходя из этого было решено использовать многопоточный поиск внутри нижнего уровня. Программа контролирует максимальное количество одновременно запущенных потоков при помощи iCurrentRun и не дает запустить более 10. Задержка между попытками запуска задана в расчете на 0.1 секунду (равна 10*100=1 секунда). Для синхронизации нескольких потоков и гарантии, что новый поток получит входной параметр правильно используется m_Processed. Переменная m_AllExited сигнализирует о завершении последнего потока. Через strScanDir передается в поток имя каталога, в котором нужно искать. В поток через pParam передается ссылка на интерфейс, вызвавший поток. Через эту ссылку можно передать сообщение интерфейсу от потока.

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

Рекомендации: ограничение потоков необходимо для того, чтобы программа вообще смогла завершиться. При бесконечном количестве - она виснет напрочь. При маленьком (например 1 - выполняется долго). Оптимальное количество 5-10 потоков. Ставить 400-500 штук тоже не следует - больше чем может вытянуть диск, вы из него выжать не сможете. Мой пример после этого стал работать не 2 часа, а 30-40 минут. Загрузка процессора небольшая.

#define EXTENSION txt
static CEvent m_Processed;	//event semaphore
static CEvent m_AllExited;	//event semaphore
static CString strScanDir;	//global parameter: directory
static long iCurrentRun;	//current threads running
static CString strExt;	//extension of files to look for

void CDialogApp::Recurse(LPCTSTR pstr)	//name of root directory to search in
{
	CString str, tmpStr;
	int iThreads, iThreadRetry;
	CFileFind finder;
	
	strExt=EXTENSION;

	iThreads=10;		//threads to start
	iThreadRetry=10;		//retry to start thread in 0.1 seconds
				// build a string with wildcards
	CString strWildcard(pstr);
	strWildcard += _T("\\*");	//just only directories
				// start working for files
	BOOL bWorking = finder.FindFile(strWildcard);
	
	while (bWorking)
	{
		bWorking = finder.FindNextFile();
		if (finder.IsDots())	// skip . and .. files; otherwise, we'd recur infinitely!
		continue;

		// if it's a directory, search into it
		str = finder.GetFilePath();
		if (finder.IsDirectory())
		{
			//start thread for finding
			m_Processed.ResetEvent();
			strScanDir=str;	//parameter for search - directory name
					//don't make more threads?
			if(iCurrentRun>=iThreads)
				while(iCurrentRun>=iThreads)
					Sleep(iThreadRetry*100);
			CWinThread* myWinThread = AfxBeginThread(*ProcessScan, GetSafeHwnd());
			WaitForSingleObject(m_Processed,INFINITE);
		}
	}
	finder.Close();			//stop looking for files
}

UINT ProcessScan(LPVOID pParam)
{
	CString strDir;
	strDir=strScanDir;
	m_AllExited.ResetEvent();	//just entered thread
	m_Processed.SetEvent();	//process main thread
				//process directory
	InterlockedIncrement((long*)&iCurrentRun);	//current thread running counter increment
				//PostMessage((HWND) pParam, WM_REFRESH_STATUS, 0,0);
				//You can post any message to the interface
	CCriticalSection m_Lock;	//critical section object
	CFileFind findFile;
	
	if(strExt.Left(1)==".")
		strExt=strExt.Mid(2);	//cut "." at left position, if exists
	strDir += _T("\\*.");		//only "*.<extension>"
	strDir +=strExt;
				// start working for files
	BOOL bWorking = findFile.FindFile(strDir);
	while (bWorking)
	{
		bWorking = findFile.FindNextFile();
		if (findFile.IsDots())		// skip . and .. files; otherwise, we'd recur infinitely!
		continue;
		if(!findFile.IsDirectory())		//this is a file
		{
			str=findFile.GetFileName();
						//this's a file!
						//.... process it!
			m_Lock.Lock();		//block code until we calculate something
						// do something, while blocking code
						//You don't have to use CriticalSection!
						//I used it only because of unsafe calculations
			m_Lock.Unlock();		//unblock code
		}
	}
	
	InterlockedDecrement((long*)&iCurrentRun);
	if (iCurrentRun==0)
		m_AllExited.SetEvent();		//exited all threads?
	return 0;
}

Шаг прислал Vlad Mogilevsky.


Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Каев Артем - 27.02.2001