Russian Qt Forum
Ноябрь 01, 2024, 08:30 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Одновременный запуск нескольких потоков  (Прочитано 10823 раз)
SeverusSnape
Гость
« : Сентябрь 13, 2011, 10:54 »

Добрый день! Начал разбираться с потоками и возникло несколько вопросов:

Суть в следующем. Организовываю в программе поиск по файлам, по содержимому. Составляю список папок в которых необходимо вести поиск и для каждой папки из этого списка создаю поток, в котором осуществляю поиск. Все работает, все отлично, но потоки хотел бы запустить одновременно, а не по очереди. Может быть чего-то не понимаю – подскажите пожалуйста, каким образом можно это организовать.

Код:
void SearchWidget::searchFiles(QDir & dir)
{
    QStringList lstDirs = dir.entryList(QDir::Dirs |
    QDir::AllDirs |
    QDir::NoDotAndDotDot);
   
   
    foreach (QString entry, lstDirs)
    {
        QString entryAbsPath = dir.absolutePath() + "/" + entry;
        FasterSearch *fastSearch = new FasterSearch(this);
        fastSearch->setSearchDir(entryAbsPath);       
fastSearch->setFileTypesFilter(“*.txt”);
        fastSearch->setContainTextFilter(“good”);
        fastSearch->searchDir();               
        connect(fastSearch,SIGNAL(foundFile(QString)),this,SLOT(addItem(QString)));
        connect(fastSearch,SIGNAL(finished()),this,SLOT(deleteLater()));
    }
}

Fastersearch.cpp
Код:
FasterSearch::FasterSearch(QObject *parent)
: QThread(parent)
{
}

FasterSearch::~FasterSearch()
{
stopSearching();
}

void FasterSearch::setSearchDir(const QString &path)
{
    searchPath = path;
}
void FasterSearch::setFileTypesFilter(const QString &types)
{
    typesList = types;
}
void FasterSearch::setContainTextFilter(const QString &filter)
{
    containTextFilter = filter;
}
void FasterSearch:: endSearching()
{
        searchPath.clear();
        typesList.clear();
        containTextFilter.clear();
        emit searchFinished();
}
void FasterSearch::run()
{
     QDir dir(j_searchPath);
    searchFiles(dir);
        endSearching();
}

void FasterSearch::waitForFinished()
{
while(isRunning());
}

void FasterSearch::searchDirDir()
{
        if(!isRunning()) start();
}

void FasterSearch::searchFiles(QDir & dir)
{
    QStringList lstDirs = dir.entryList(QDir::Dirs |
    QDir::AllDirs |
    QDir::NoDotAndDotDot);
   
    foreach (QString entry, lstDirs)
    {
        QString entryAbsPath = dir.absolutePath() + "/" + entry;
        QDir dr(entryAbsPath);
        searchFiles(dr);
    }
   
    QStringList filters;
    QString filt = typesList;
    filters << filt;
   
}
    QStringList lstFiles = dir.entryList( filters , QDir::Files);
    foreach (QString entry, lstFiles)
    {
        QString entryAbsPath = dir.absolutePath() + "/" + entry;
     
        if (!containTextFilter.isEmpty())
        {
            QFile file(entryAbsPath);
            if (file.open(QIODevice::ReadOnly))
            {
                QString line;
                QTextStream in(&file);
               
                while (!in.atEnd())
                {
                    line = in.readLine();
                    if (line.toLower().contains(containTextFilter.toLower()))
                    {
                        emit foundFile(entryAbsPath);
                        break;
                    }
                }
                file.close();
                line.clear();
            }
    }
    else
        emit foundFile(entryAbsPath);
   
    }
}
void FasterSearch::stopSearching()
{
if(isRunning())
{
            terminate();
            endSearching();
}
}
 
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #1 : Сентябрь 13, 2011, 12:38 »

В любом случае сначала надо создать массивы/контейнеры всех файлов и всех рабочих ниток. Потом можно по-всякому, проще всего каждой нитке дать часть файлов (напр индекс в массиве файлов + кол-во) и запустить.

Не балуйтесь с QThread::terminate (не ищите неприятностей, дайте нитке нормально выйти).
deleteLater не годится, свяжите finished со слотом где подсчитывается число отстрелявшихся ниток, ну а там уже когда все завершились разберетесь 

 
Записан
SeverusSnape
Гость
« Ответ #2 : Сентябрь 13, 2011, 15:51 »

Спасибо за ответ! Учту. Раз представилась такая возможность, уточню еще вот что - а каким образом можно ускорить создание массива папок? Могу ли я использовать для этой цели потоки. Простите за глупые вопросы, но с потоками только начинаю знакомиться
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Сентябрь 13, 2011, 16:38 »

Спасибо за ответ! Учту. Раз представилась такая возможность, уточню еще вот что - а каким образом можно ускорить создание массива папок?
Эту задачу лучше отложить - неясно удастся ли здесь что-то выиграть (может быть и наоборот). В общих чертах: есть N папок/мамок. Запускаются M ниток каждой из которых дается часть из N. Каждая находит какое-то кол-во (напр 100) новых папок. Обновляем массив N, повторяем пока не останется ни одной.

Вообще не нужно слишком резво все "распоточивать"  Улыбающийся  Это непросто (если делать хорошо)
Записан
daimon
Гость
« Ответ #4 : Январь 19, 2012, 04:22 »

есть чуток проще реализация

Код
C++ (Qt)
 
QStringList CTranslateUtilRun::getListFilesFind( const QString &pathr, QStringList filters )
{
m_processError = ETE_WAITFORMINGFILELIST;
qApp->processEvents();
 
QString path;
if(pathr.isEmpty()) path = QDir::currentPath();
else path = pathr;
QDir dir(path);
 
if(!dir.exists())
{
emit isErrorDir(true);
 
 
return QStringList();
}
 
QStringList listFiles = QStringList();
 
 
foreach (QString file, dir.entryList(filters))
{
listFiles << QFileInfo(dir, file).absoluteFilePath();
 
}
 
foreach (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
listFiles << getListFilesFind(path + QDir::separator() + subDir, filters);
 
}
 
 
return listFiles;
}
 
 

Основная функция запустил один раз - пойдёт рекурсия и забъётся список нужными именами файлов

Как переделать под потоки? Логично при вызове рекурсии создавать новый поток и передавать параметры для поиска в текущей папке (потоки будут работать как бы по очереди, хотя не совсем)

Нужно ли лепить мютекст на список и вообще, что толком такое мютекс (вроде остановка), ведь тогда теряется суть потока (они начинают работать по очереди)? Знаю, что одновременно будет писаться разными потоками мой список, для этого есть несколько решений:
1 убрать пушанье в список, а сделать ресайз и вставлять с последнего элемента, когда список пришёл, и до конца для этого потока (проблема - частый ресайз убъет производительность), на момент ресайза заблокировать список
2 убрать пушанье на каждом шаге главного списка, а для этих целей сделать локальный список и его, по концу работы потока, вставить в главный список в конец (на момент вставки заблокировать список другим потокам)
3. как-то разрулить вставку мютексами и семафорами, я это вообще не знаю

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

это всё теория толком реализации по блокировке списка не знаю
пока ещё в функции и нет главного списка, то есть последняя вставка и сформирует весь список
« Последнее редактирование: Январь 19, 2012, 04:45 от daimon » Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #5 : Январь 19, 2012, 10:29 »

Код:
        fastSearch->searchDir();                
        connect(fastSearch,SIGNAL(foundFile(QString)),this,SLOT(addItem(QString)));
        connect(fastSearch,SIGNAL(finished()),this,SLOT(deleteLater()));
Почему вы связываете слоты уже после запуска потока? Ведь может так случиться что до первого коннекта уже будет найден файл, но сигнала об этом не поступит.
Лучше так:
Код:
        connect(fastSearch,SIGNAL(foundFile(QString)),this,SLOT(addItem(QString)));
        connect(fastSearch,SIGNAL(finished()),this,SLOT(deleteLater()));
        fastSearch->searchDir();           
Еще копайте в сторону пулов потоков. ThreadPool. Я не знаю, есть ли такое в Qt, но в свое время активно юзал в QNX - полезная штука, впрочем реализовать нечто подобное не так уж сложно.
Записан
daimon
Гость
« Ответ #6 : Январь 21, 2012, 02:20 »

Код
C++ (Qt)
void FasterSearch::searchFiles(QDir & dir)
{
   QStringList lstDirs = dir.entryList(QDir::Dirs |
   QDir::AllDirs |
   QDir::NoDotAndDotDot);
 
   foreach (QString entry, lstDirs)
   {
       QString entryAbsPath = dir.absolutePath() + "/" + entry;
       QDir dr(entryAbsPath);
       searchFiles(dr);
   }
 
   QStringList filters;
   QString filt = typesList;
   filters << filt;
 
}
   QStringList lstFiles = dir.entryList( filters , QDir::Files);
   foreach (QString entry, lstFiles)
   {
       QString entryAbsPath = dir.absolute

там нестыковка, что за функция для кода, после   QStringList lstFiles = dir.entryList( filters , QDir::Files);

и нашел недостаток - создаются потоки только под количество папок самой первой вложенности, а должно ведь - новый поток, как только найдена новая папка, неважно какая вложенность поиска (тогда и получается, что поток нужно создавать в потоке)
спс
« Последнее редактирование: Январь 21, 2012, 02:45 от daimon » Записан
daimon
Гость
« Ответ #7 : Январь 21, 2012, 04:44 »

пробовал так
Код
C++ (Qt)
#include <QtGui>
 
 
class ThreadSearch: public QThread
{
Q_OBJECT
 
QString m_path;
QStringList m_filter;
QStringList m_files;
bool stop;
 
 
signals:
void foundFiles(QStringList);
 
 
 
public:
QStringList getFiles() const { return m_files; }
 
ThreadSearch()
{
 
}
ThreadSearch(QString path, QStringList filter)
{
m_path = path;
m_filter = filter;
qDebug()<<"thread";
connect(this,SIGNAL(finished()),this, SLOT(myStop()));
stop = false;
 
}
 
QString getPath() const { return m_path; }
void setPath(QString val) { m_path = val; }
 
void run()
{
this->setPriority(QThread::Priority::LowPriority);
getListFilesFind(m_path,m_filter);
 
exec();
 
}
public slots:
void setList(QStringList list)
{
m_files << list;
}
void myStop()
{
stop = true;
qDebug()<<"finished";
}
 
QStringList getListFilesFind( const QString &pathr, QStringList filters )
{
 
//qApp->processEvents();
 
QString path;
if(pathr.isEmpty()) path = QDir::currentPath();
else path = pathr;
QDir dir(path);
 
if(!dir.exists())
{
//emit isErrorDir(true);
 
 
return QStringList();
}
 
QStringList listFiles = QStringList();
 
 
foreach (QString file, dir.entryList(filters))
{
listFiles << QFileInfo(dir, file).absoluteFilePath();
 
}
 
foreach (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
ThreadSearch * thread = new ThreadSearch(path + QDir::separator() + subDir, filters);
 
thread->start();
//listFiles << thread->getListFilesFind(path + QDir::separator() + subDir, filters);
 
connect(thread,SIGNAL(foundFiles(QStringList)),this,SIGNAL(foundFiles(QStringList)));
// connect(thread,SIGNAL(foundFiles(QStringList)),this,SLOT(setList(QStringList)));
 
 
}
 
emit foundFiles(listFiles);
 
emit finished();
 
//setList(listFiles);
//qDebug()<<listFiles;
 
return listFiles;
}
};
 
////
 
использую так
 
#include "searchwidget.h"
 
SearchWidget::SearchWidget(QWidget *parent, Qt::WFlags flags)
: QWidget(parent, flags)
{
ui.setupUi(this);
searchFiles(QDir("F:/Projects/Visual Studio 2008"));
}
 
SearchWidget::~SearchWidget()
{
 
}
 
void SearchWidget::searchFiles( QDir & dir )
{
thread = new ThreadSearch("F:/Projects/Visual Studio 2008",QStringList()<<"*.h"<<"*.cpp");
thread->start();
 
connect(thread,SIGNAL(foundFiles(QStringList)),this,SLOT(addItems(QStringList)));
connect(thread,SIGNAL(finished()),this,SLOT(addItem()));
}
 
void SearchWidget::addItem( )
{
setWindowTitle("ok");
// ui.textEdit->append(str);
//throw std::exception("The method or operation is not implemented.");
}
 
void SearchWidget::addItems( QStringList list )
{
qApp->processEvents();
 
ui.listWidget->addItems(list);
 
 
setWindowTitle(QString::number(thread->getFiles().count())+ "  " + QString::number(ui.listWidget->count()));
 
//throw std::exception("The method or operation is not implemented.");
}
 

Проблемы:

1. нереально от самого корневого потока узнать об завершении поиска
2. лагает добавление в листвиджет итемов
3. thread->getFiles().count())+ "  " + QString::number(ui.listWidget->count()) возвращают разные значение - разница на 10 элементов (загадка)
4. не могу остановить родительский поток, так как умрёт цепочка дочерних, хотя не знаю почему, они же сделаны указателями
5. если приоритет поставить высокий для потоков - падает система.

а так создаётся столько потоков, сколько папок от начала поиска и они ищут сами по себе файлы (как только есть папка - новый поток).  Что не так в логике, подскажите, спс

« Последнее редактирование: Январь 21, 2012, 04:56 от daimon » Записан
Странник
Гость
« Ответ #8 : Январь 21, 2012, 10:17 »

создавать новый поток для каждой папки - моветон. папок может оказаться несколько сотен. а если в папке нет файлов, а лишь вложенные папки? так вы еще и в скорости можете проиграть. я бы пробежался рекурсивно по дереву и создал очередь файлов, а для поиска по этим файлам использовал бы QThreadPool с числом потоков что-нибудь около QThread::idealThreadCount().
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #9 : Январь 21, 2012, 11:05 »

Вообще нет смысла делать больше чем 1 поток для поиска файлов. Это связано с тем, что проверка строки на совпадение с фильтром меньше, чем получение одной энтри от ФС. А рекурсивный обход дерева ФС не параллелится (дерево же ж). Можно выиграть, как уже упоминалось, построив рекурсивно список файлов, а потом его в несколько потоков перебирать. Это даст выигрыш для большого количества файлов, но проигрыш по памяти (хранить 50тыщ полных путей к файлам в юникоде, хм...).
Это был случай поиска по имени. Если же нам надо искать не только по имени, но и, например по дате, или по содержимому, или по миметипу (к-ый, в свою очередь, тоже лезет в содержимое файла), то тут все неоднозначно. Но ясно одно - чтение данных из файла и получение его инфы опять таки дольше, чем сравнение (дата это вообще инт64, просмотреть батарей на предмет шаблона не долго).
В результате, мы можем конечно закешировать всевозможную инфо о файле, но это даст очень малый прирост по скорости. Содержимое же файлов кешировать нельзя, читать из файлов в несколько потоков тоже смысла нет (да, я знаю про говеный планировщик диска в винде и про то что ВНЕЗАПНО писать/читать на диск/сдиска в несколько потоков быстрее чем в один).
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #10 : Январь 21, 2012, 13:46 »

А рекурсивный обход дерева ФС не параллелится (дерево же ж).
Параллелится, хотя и не просто. А вообще Вы правы - особого выигрыша здесь не поучить, все упрется в I/O. Зато как учебная multi-thread задача это подходит прекрасно (как впрочем и весь Qt) 
Записан
daimon
Гость
« Ответ #11 : Январь 21, 2012, 14:01 »

Вообще нет смысла делать больше чем 1 поток для поиска файлов. Это связано с тем, что проверка строки на совпадение с фильтром меньше, чем получение одной энтри от ФС. А рекурсивный обход дерева ФС не параллелится (дерево же ж). Можно выиграть, как уже упоминалось, построив рекурсивно список файлов, а потом его в несколько потоков перебирать. Это даст выигрыш для большого количества файлов, но проигрыш по памяти (хранить 50тыщ полных путей к файлам в юникоде, хм...).
Это был случай поиска по имени. Если же нам надо искать не только по имени, но и, например по дате, или по содержимому, или по миметипу (к-ый, в свою очередь, тоже лезет в содержимое файла), то тут все неоднозначно. Но ясно одно - чтение данных из файла и получение его инфы опять таки дольше, чем сравнение (дата это вообще инт64, просмотреть батарей на предмет шаблона не долго).
В результате, мы можем конечно закешировать всевозможную инфо о файле, но это даст очень малый прирост по скорости. Содержимое же файлов кешировать нельзя, читать из файлов в несколько потоков тоже смысла нет (да, я знаю про говеный планировщик диска в винде и про то что ВНЕЗАПНО писать/читать на диск/сдиска в несколько потоков быстрее чем в один).
проблема та состоит, чтобы составить это массив, это ж нужно обойти все те папки))) - время, время
Записан
daimon
Гость
« Ответ #12 : Январь 21, 2012, 14:09 »

пробовал так
Код
C++ (Qt)
#include <QtGui>
 
 
class ThreadSearch: public QThread
{
Q_OBJECT
 
QString m_path;
QStringList m_filter;
QStringList m_files;
bool stop;
 
 
signals:
void foundFiles(QStringList);
 
 
 
public:
QStringList getFiles() const { return m_files; }
 
ThreadSearch()
{
 
}
ThreadSearch(QString path, QStringList filter)
{
m_path = path;
m_filter = filter;
qDebug()<<"thread";
connect(this,SIGNAL(finished()),this, SLOT(myStop()));
stop = false;
 
}
 
QString getPath() const { return m_path; }
void setPath(QString val) { m_path = val; }
 
void run()
{
this->setPriority(QThread::Priority::LowPriority);
getListFilesFind(m_path,m_filter);
 
exec();
 
}
public slots:
void setList(QStringList list)
{
m_files << list;
}
void myStop()
{
stop = true;
qDebug()<<"finished";
}
 
QStringList getListFilesFind( const QString &pathr, QStringList filters )
{
 
//qApp->processEvents();
 
QString path;
if(pathr.isEmpty()) path = QDir::currentPath();
else path = pathr;
QDir dir(path);
 
if(!dir.exists())
{
//emit isErrorDir(true);
 
 
return QStringList();
}
 
QStringList listFiles = QStringList();
 
 
foreach (QString file, dir.entryList(filters))
{
listFiles << QFileInfo(dir, file).absoluteFilePath();
 
}
 
foreach (QString subDir, dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot))
{
ThreadSearch * thread = new ThreadSearch(path + QDir::separator() + subDir, filters);
 
thread->start();
//listFiles << thread->getListFilesFind(path + QDir::separator() + subDir, filters);
 
connect(thread,SIGNAL(foundFiles(QStringList)),this,SIGNAL(foundFiles(QStringList)));
// connect(thread,SIGNAL(foundFiles(QStringList)),this,SLOT(setList(QStringList)));
 
 
}
 
emit foundFiles(listFiles);
 
emit finished();
 
//setList(listFiles);
//qDebug()<<listFiles;
 
return listFiles;
}
};
 
////
 
использую так
 
#include "searchwidget.h"
 
SearchWidget::SearchWidget(QWidget *parent, Qt::WFlags flags)
: QWidget(parent, flags)
{
ui.setupUi(this);
searchFiles(QDir("F:/Projects/Visual Studio 2008"));
}
 
SearchWidget::~SearchWidget()
{
 
}
 
void SearchWidget::searchFiles( QDir & dir )
{
thread = new ThreadSearch("F:/Projects/Visual Studio 2008",QStringList()<<"*.h"<<"*.cpp");
thread->start();
 
connect(thread,SIGNAL(foundFiles(QStringList)),this,SLOT(addItems(QStringList)));
connect(thread,SIGNAL(finished()),this,SLOT(addItem()));
}
 
void SearchWidget::addItem( )
{
setWindowTitle("ok");
// ui.textEdit->append(str);
//throw std::exception("The method or operation is not implemented.");
}
 
void SearchWidget::addItems( QStringList list )
{
qApp->processEvents();
 
ui.listWidget->addItems(list);
 
 
setWindowTitle(QString::number(thread->getFiles().count())+ "  " + QString::number(ui.listWidget->count()));
 
//throw std::exception("The method or operation is not implemented.");
}
 

Проблемы:

1. нереально от самого корневого потока узнать об завершении поиска
2. лагает добавление в листвиджет итемов
3. thread->getFiles().count())+ "  " + QString::number(ui.listWidget->count()) возвращают разные значение - разница на 10 элементов (загадка)
4. не могу остановить родительский поток, так как умрёт цепочка дочерних, хотя не знаю почему, они же сделаны указателями
5. если приоритет поставить высокий для потоков - падает система.

а так создаётся столько потоков, сколько папок от начала поиска и они ищут сами по себе файлы (как только есть папка - новый поток).  Что не так в логике, подскажите, спс



сделал замер по времени, где-то проигрую с потоками в 2 раза по сравнению с рекурсией циклов, мда вот так параллельность
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Январь 21, 2012, 14:58 »

Что не так в логике, подскажите, спс
Грубо говоря Вы делаете так: получили dir, создали новую нитку и дали ей этот dir. Такое размножение ниток плохо, о чем уже не раз говорилось, и в этой теме тоже.

Здесь хорошо подходит "stealing" техника (нитка у которой нет задач начинает "воровать" задачи из очереди другой).
Записан
daimon
Гость
« Ответ #14 : Январь 21, 2012, 15:40 »

Что не так в логике, подскажите, спс
Грубо говоря Вы делаете так: получили dir, создали новую нитку и дали ей этот dir. Такое размножение ниток плохо, о чем уже не раз говорилось, и в этой теме тоже.

Здесь хорошо подходит "stealing" техника (нитка у которой нет задач начинает "воровать" задачи из очереди другой).


это как понять, воровать и нитка без задач?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.149 секунд. Запросов: 23.