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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Подскажите как сделать многопоточную загрузку из файлов  (Прочитано 5317 раз)
kdm
Гость
« : Апрель 15, 2010, 19:26 »

(честно говоря не знал, где такую тему создавать, подумал, что таким, как я - сюда)

Дело состоит в том, что я пишу первое в жизни многопоточное приложение.
С потоками разбирался с книжкой по Qt, Примерам, Assistant'у, но так и не понимаю, как их использовать в конкретном для меня случае.
Прошу аудиторию форума помочь разобраться в этой ерунде, вторую неделю сижу, кучу листов изрисовал, так и не понял.

Вот для накидал небольшую промежуточную программку, которая по нажатию кнопки "загрузить" открывает диалоговое окно с файлами, а потом открывается диалоговое окно с label'ами на которых отображены текущий загружаемый файл и процент проделанной операции. Последнее диалоговое окно запускает один за одним потоки (то есть когда один отработает, он удаляется и создается и запускается другой и так далее). Когда перебраны все имена файлов, то диалоговое окно закрывается.

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

Код:
Код:
class TLoadProgress : public QDialog
{
Q_OBJECT
    // Обяъвления элементов управления пропущены за экономией места в посте

    QStringList FileNames;
    int FileIndex;
    bool ThreadCreated;

    TLoadThread* Thread;
public:
    explicit TLoadProgress (QStringList& file_names, QWidget *parent = 0);
    ~TLoadProgress ();

public slots:
    void on_thread_stop ();
    void on_thread_progress (int value);

};

TLoadProgress :: TLoadProgress(QStringList& file_names, QWidget *parent)
    : QDialog(parent), FileNames(file_names), FileIndex(0), ThreadCreated(false)
{
    // Создание элементов управления пропущены за экономией места в посте

    // Dialog Buttons
    QHBoxLayout* layoutDialogButtons = new QHBoxLayout();
    layoutMain->addLayout(layoutDialogButtons);

    layoutDialogButtons->addWidget(btnOK = new QPushButton("OK", this));
    layoutDialogButtons->addWidget(btnCancel = new QPushButton("Cancel", this));

    on_thread_stop();
}

void TLoadProgress :: on_thread_stop ()
{
    qDebug() << "on thread stop";
    if (ThreadCreated)
    {
        if (Thread->getExitCode() == 1)
        {
            qDebug() << "on thread stop: Error On Exit Code";
        }
        qDebug() << "on thread stop: delete thread";
        delete Thread;
        ThreadCreated = false;
    };

    if (FileIndex < FileNames.count())
    {
        Thread = new TLoadThread(FileNames.at(FileIndex++));
        ThreadCreated = true;
        connect(Thread, SIGNAL(finished()), this, SLOT(on_thread_stop()));
        connect(Thread, SIGNAL(progress(int)), this, SLOT(on_thread_progress(int)));
        Thread->start();
    }
    else
        done(0);
}

TLoadProgress :: ~TLoadProgress ()
{
    if (ThreadCreated)
    {
        qDebug() << "progress destructor: thread delete";
        Thread->deleteLater();
    }
}

void TLoadProgress :: on_thread_progress (int value)
{
    lblProgress->setText(QString().setNum(value));
}

Код:
class TLoadThread : public QThread
{
    Q_OBJECT

    QString FileName;
    QFile File;
    QTextStream TextStream;
    int ExitCode;
    bool StopRequested;

public:
    explicit TLoadThread (QString file_name, QObject *parent = 0);
    void run ();
    int getExitCode ();
    void requestStop ();
    ~TLoadThread ();

signals:
    void progress (int);

public slots:
};

TLoadThread :: TLoadThread(QString file_name, QObject *parent)
    : QThread(parent), FileName(file_name), ExitCode(0), StopRequested(false)
{
    File.setFileName(FileName);
    File.open(QIODevice::ReadOnly);

    TextStream.setDevice(&File);
}

void TLoadThread :: run ()
{
    qDebug() << "thread run: Continue Read File From Stream " << FileName;
    for (double i = 0.0; /*!StopRequested &&*/ i < 100.0; i+=0.001)
    {
        emit progress(i);
    }
    ExitCode = 0;
}

int TLoadThread :: getExitCode ()
{
    return ExitCode;
}

void TLoadThread :: requestStop ()
{
    StopRequested = true;
}

TLoadThread :: ~TLoadThread ()
{
    qDebug() << "thread destructor: thread destroy";
    File.close();
}

Код:
void TMainWindow :: on_btnFilesOpen_clicked ()
{
    QFileDialog dlg(this, "Open File");
    dlg.setFileMode(QFileDialog::ExistingFile);
    dlg.setFilter("Data Files (*.*)");
    dlg.setAcceptMode(QFileDialog::AcceptOpen);
    dlg.setFileMode(QFileDialog::ExistingFiles);

    if (dlg.exec())
    {
        QStringList file_names = dlg.selectedFiles();

        TLoadProgress* LoadProgress = new TLoadProgress(file_names, this);
        LoadProgress->exec();
    }

}

И еще совсем не понятные для меня вещи:
1) Почему когда вызывается запрос на завршение потока, то если exec() внутри run() не выполнялся, то функций ничего не делает, приходится свою функцию писать requestStop, которая переменную нужную выставляет в true, чтобы алгортм завершился. В Билдере же уже есть такая переменная и функция тоже есть, которая ее изменяет.
2) Зачем вообще этот exec(), если выполнение потока переходит во его внутреннюю очередь сообщений и там замирает, пока новое событие не появится. А что если надо чтобы поток постоянно что-то выполнял? Тогда получается все надо делать без exec().
3) Почему, когда в этой маленькой программе кнопкой [X] в заголовке закрываю окно с идикаторами прогресса, то окно родительское замерзает на определенное время (полагаю, на которое работает run() в потоке).

Пожалуйста, выскажите все свои замечания, идеи и рекомендации.

Если я тут написал какой-то бред, не ругайтесь, голова уже едет.

На всякий случай выложил исходники в сети: http://file.qip.ru/file/125946488/df6a8d23/ThreadedLoad2.html
« Последнее редактирование: Апрель 15, 2010, 19:59 от kdm » Записан
alexman
Гость
« Ответ #1 : Апрель 15, 2010, 20:32 »

Даже не читал текст (вернее прочитал только заголовок), но считаю, что нет смысла делать многопоточность для данной задачи, так как все скорее всего упрется в быстродействие жесткого диска. Многопоточность применяется, например, в вычеслительных задачах! Хотя если файлы на разных носителях, то смысл появляется...
« Последнее редактирование: Апрель 15, 2010, 20:34 от alexman » Записан
Пантер
Administrator
Джедай : наставник для всех
*****
Offline Offline

Сообщений: 5876


Жаждущий знаний


Просмотр профиля WWW
« Ответ #2 : Апрель 15, 2010, 20:59 »

alexman, смысл есть, если не хочешь заморозки гуя.
По теме, сейчас влом читать, завтра/послезавтра гляну. kdm, у меня тут проектик был - PantherCommander, можешь найти его на форуме и посмотреть как я в потоке копирование делал. Код, конечно, УГ, но смысл понять, думаю, сможешь.
Записан

1. Qt - Qt Development Frameworks; QT - QuickTime
2. Не используйте в исходниках символы кириллицы!!!
3. Пользуйтесь тегом code при оформлении сообщений.
kdm
Гость
« Ответ #3 : Апрель 15, 2010, 21:05 »

Пантер, О пасибо! Ща буду искать.
Во, нашел, буду разбираться.
« Последнее редактирование: Апрель 15, 2010, 21:41 от kdm » Записан
alexman
Гость
« Ответ #4 : Апрель 15, 2010, 21:34 »

Об потоке отдельном от гуя - это понятно! Имелось в виду, что не стоит создавать тучу потоков на чтение одного файла.
Записан
kdm
Гость
« Ответ #5 : Апрель 15, 2010, 21:47 »

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

Вообще, это дикая идея, но это единственное, что пришло мне в голову. Вразумите, пожалуйста, если я где-то дюже криво загнул.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Апрель 16, 2010, 17:34 »

Простой вариант приаттачен. Конечно, можно и по-другому. Напр. запускать нитку 1 раз и передать ей список файлов. Или сделать без флага mAborted, через сигнал
Записан
kdm
Гость
« Ответ #7 : Апрель 16, 2010, 18:20 »

АаааАагромное спасибо :] Подмигивающий

Только одного не понял, зачем потоку exec, если при exec поток уходит в цикл обработки событий и там остается до появления нового события.
« Последнее редактирование: Апрель 16, 2010, 18:22 от kdm » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Апрель 16, 2010, 18:39 »

Только одного не понял, зачем потоку exec, если при exec поток уходит в цикл обработки событий и там остается до появления нового события.
Запускать exec никто не заставляет, в данном примере его просто нет. А вообще может быть удобно - запустили несколько ниток и подкидываете им работенку по мере ее поступления используя обычные сигналы
Записан
kdm
Гость
« Ответ #9 : Апрель 16, 2010, 21:18 »

О, точно
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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