Версия для печати темы
Форум на CrossPlatform.RU _ Qt Общие вопросы _ Exec threads etc...
Автор: me 17.2.2009, 23:03
Доброго времени суток..
Не так давно начал разбираться с взаимодействием потоков в qt и осознал что оч мощно запутался.
Есть задача: написать многопотоный ftp клиент который парсит файлик с заданиями и закачивает с разных ftp ресурсов файлы. Многопоточный просто для того чтобы самому в этом хорошенько разобраться. Решил организовать это следующим образом на каждое задание создаётся поток и далее вся работа через QFtp. Насколько я понял QFtp асинхронный, а так как мне требуется помимо непосредственно работы с ftp в этом же потоке обрабатывать некоторые действия со скачанными файлами то требуется вынести это в соотвествующие слоты. Не понятно мне стало вот что:
- насколько я понимаю слоты прикреплённые к соотвествующим сигналам из Qftp НЕ будет выполнятся до тех пор пока не будет вызван exec в потоке? Но тогда я так понима.ю мне придётся как минимум обращаться к потоку из главного потока дважды. Первый раз на старт потока а второй на запуск задания (просто слот с перечнем комманд на коннект и на скачку)
- получается что всю работу с ftp надо будет полностью выносить в отдельные слоты, что видится мне несколько грамоздким.
Вообщем требуется квалифицированная помощь)))
з.ы. Заранее хочу сказать, что листал книги по qt, читал документацию, но разобрался не во всём и эти моменты для меня туманны, так что просьба к великому гуглу не отсылать)))
з.з.ы Какой-нибудь простенький пример будет оч кстати.. ток главный смысл в том что программа будет автоматизированна и без участие человека и без гуи... так что явный инициатор QFtp c точки зрения запуска заданий отсуствует..
Заранее спасибо!
Автор: ViGOur 17.2.2009, 23:36
Работу с FTP не нужно выносить в отдельные слоты.
Давай попробуем разделить изучение того, что тебе нужно на шаги:
1. Для лучшего понимания почитай http://www.doc.crossplatform.ru/qt/4.3.2/signalsandslots.html () (если еще не читал)
2. Глянь пример работы с ftp: http://www.doc.crossplatform.ru/qt/4.3.2/network-ftp.html. А за одно и класс QFtp, для получения файла нужно использовать: http://www.doc.crossplatform.ru/qt/4.3.2/qftp.html#get. По данному примеру и сделай свою реализацию, пускай и консольную, GUI в примере чисто для визуализации.
3. Прочитай http://www.doc.crossplatform.ru/qt/4.3.2/threads.html.
А теперь, если ты на отлично понял 1, 2, 3 пункты, попробуй 'нарисовать' более ясную картину твоей реализации. Потому как у тебя явно полная каша в голове.
Автор: me 18.2.2009, 0:17
Цитата(ViGOur @ 17.2.2009, 23:36)
А теперь, если ты на отлично понял 1, 2, 3 пункты, попробуй 'нарисовать' более ясную картину твоей реализации. Потому как у тебя явно полная каша в голове.
Вы не поверите.. про потоки и слоты я читал.. и пример тоже смотрел. Причём после предложения прочитать ещё раз я честно перечитал это ещё раз дабы освежить в памяти.
В голове у меня действительно сейчас небольшая каша..((( причём гланым образом из-за того что я не понимаю как конкретно работает асинхронный QFtp. Ведь как я понял функции аля connect и get сразу же возвращают значение при этом не блокируют поток... Вообще самое печальное что я не так давно делал и клиентское приложение и серверное приложение где было по потоку на соединение.. но там вроде всё получилось но видать не доразобрался до конца.
Попробую объяснить что я хотел бы сделать:
Главный поток парсит файл. На каждое задание он создаёт поток и передаёт конструктору потока адрес сервера, логин пароль, путь к папке на сервере, путь к локальной папке.
В потоке происходит подключение к ftp серверу. Считывается листинг директории (с вхождением в глубь) и проверяется по маске нужные нам файлы. После этого файлы скачиваются.
Неясный мне момент вот в чём:
void MyThread::run()
{
QFtp ftp = new QFtp;
ftp->connectToHost("ftp.trolltech.com");
ftp->login();
ftp->list();
......
ftp->close();
exec();
}
1) list будет испускать сигнал о каждой записи. Предположим что мы сделали коннект этого сигнала со слотом класса MyThread который их как-то обрабатывает. Скажите сигнал listInfo будет обрабатываться после exec или до??? или exec вообще здесь никак не влияет? Если бы речь шла о функциях которые отрабатывают сразу всё было бы ясно.. но здесь ведь асинхронная работа.. Или exec нам в данном случае вообще не нужен?
2) получается что если там где стоит .... я бы захотел как-то обрабатывать файлы которые скачал (будем считать что после list идёт скачка файлов) то тогда мне надо выносить это в слот, потому что скачивание файлов тоже асинхронно и мы можем перейти к выполнению этих операций тогда когда файлы ещё по существую не скачались.
3) добавлю к вопросу 1. Когда мы будем извещаться о том отработала например функция connectToHost (cоотвествующий сигнал что функция done)? в произвольный момент времени после её вызова или строго после exec?
з.ы. exec как много в этом звуке=((((
Автор: SABROG 18.2.2009, 0:37
Цитата(Гость_me_* @ 18.2.2009, 0:17)
Скажите сигнал listInfo будет обрабатываться после exec или до???
Во время, скорее всего. Я так понял класс хоть и ассинхронный, но он не поточный, а событийный (event loop). Если ты застрянешь в каком-нибудь методе надолго и возвращение не будет в eventloop, то и слоты твои вызываться не будут.
Автор: me 18.2.2009, 1:01
Цитата(SABROG @ 18.2.2009, 0:37)
Цитата(Гость_me_* @ 18.2.2009, 0:17)
Скажите сигнал listInfo будет обрабатываться после exec или до???
Во время, скорее всего. Я так понял класс хоть и ассинхронный, но он не поточный, а событийный (event loop). Если ты застрянешь в каком-нибудь методе надолго и возвращение не будет в eventloop, то и слоты твои вызываться не будут.
Эмм... не понял. Что такое поточный класс?
"Если ты застрянешь в каком-нибудь методе надолго и возвращение не будет в eventloop, то и слоты твои вызываться не будут." - тоже не понял что вы имели ввиду(
Автор: SABROG 18.2.2009, 1:39
Цитата(Гость_me_* @ 18.2.2009, 1:01)
Эмм... не понял. Что такое поточный класс?
Класс, который использует QThread, чтобы добиться асинхронности.
Цитата(Гость_me_* @ 18.2.2009, 1:01)
тоже не понял что вы имели ввиду(
Что такое event loop читал в документации?
Автор: me 18.2.2009, 9:16
Цитата(SABROG @ 18.2.2009, 1:39)
Что такое event loop читал в документации?
Вечером тормозил что-то... Тоесть судя по разсуждениям сигналы, испускаемый от QFtp смогут обрабатываться только после выполнения exec в потоке.. и без exec обрабатываться не будут..
Автор: SABROG 18.2.2009, 9:38
Это из-за того, что QAbstrackSocket не блокирующий. Не знаю как в linux'е, а в виндах асинхронность добивается путем принятия WM_сообщений окном от ОС. Естественно, если в этот момент выполняется какой-нибудь метод, то сама программа не получит сообщение о том, что пришли какие-то данные, пока не произойдет возврат в основной event loop, где Qt сможет забрать сообщения от ОС с помощью API.
Вообще, если сравнивать гуишный и консольный вариант программ Qt, то консольные программы тоже бывают разные. Тот вариант консольного приложения, что делает Qt не будет работать в DOS'e например. Это обычное Win32 console приложение, где главным окном является окно консоли и оно естественно поддерживает windows messages. В *nix'ах это уже другое приложение, оно будет работать без X-Server'а, там механизм событий уже иной.
Автор: me 18.2.2009, 9:51
Цитата(SABROG @ 18.2.2009, 9:38)
Это из-за того, что QAbstrackSocket не блокирующий. Не знаю как в linux'е, а в виндах асинхронность добивается путем принятия WM_сообщений окном от ОС. Естественно, если в этот момент выполняется какой-нибудь метод, то сама программа не получит сообщение о том, что пришли какие-то данные, пока не произойдет возврат в основной event loop, где Qt сможет забрать сообщения от ОС с помощью API.
Вроде всё стало понятно, спасибо!
Эх.. а с блокирующими сокетами было бы всё несколько проще..
Автор: Константин 18.2.2009, 14:46
Гость_me_*,
для испускания сигналов петля событий не нужна. т.е. вообще. петля событий потоку необходима, если в этом потоке требуется асинхронно выполнять слоты.
твоя задача решается довольно просто, если разобраться со взаимодействием потоков в кутэ.
/* хотел привести ссылку на почитать, но не нашёл ( */
Автор: SABROG 18.2.2009, 14:58
Цитата(Гость_me_* @ 18.2.2009, 9:51)
Эх.. а с блокирующими сокетами было бы всё несколько проще..
Для этого были созданы такие методы:
bool waitForConnected ( int msecs = 30000 )
bool waitForDisconnected ( int msecs = 30000 )
virtual bool waitForReadyRead ( int msecs = 30000 )
Т.е. все блокируется на 30 секунд, а там уже можешь либо повторить попытку, либо пользователю выдать еrror.
Автор: me 18.2.2009, 15:14
Цитата(Константин @ 18.2.2009, 14:46)
Гость_me_*,
петля событий потоку необходима, если в этом потоке требуется асинхронно выполнять слоты.
Так у меня и получается что на асинхронный сигнал QFtp надо вызывать слот в этом же потоке.. (например как я уже говорил для анализа и работы листинга директории)
Автор: SABROG 18.2.2009, 15:45
Цитата(Гость_me_* @ 18.2.2009, 15:14)
Так у меня и получается что на асинхронный сигнал QFtp надо вызывать слот в этом же потоке.. (например как я уже говорил для анализа и работы листинга директории)
Соединяй нужные сигналы от QFtp со слотами QThread и выполняй exec() в run(). Все, что будет одти до exec - выполняется один раз при запуске потока, потом exec() входит в "бесконечный" цикл, т.е. возврата из run() не будет до тех пор, пока не будет выход из eventloop, а это произойдет уже при завершении потока. Таким образом все слоты будут обрабатываться не до и не после exec, а во время.
QFtp::list() и прочие команды возвращают свой уникальный ID команды. Можно использовать этот номер, чтобы отслеживать, когда команда завершится. До завершения команды у тебя будут приходить сигналы listInfo(), можно его обрабатывать и заполнять свой список нужным образом, отсортировывая не нужное. В конце придет сигнал commandFinished(int id), IDшник сравниваешь с IDшником возвращенным от list()'a и если это он, то начинаем выкачивать файлы по одному из своего списка. Удаляя выкаченные файлы из списка. На завершение выкаченных файлов тоже будут приходить сигналы со своим id, там ты и будешь удалять из своего списка то, что скачалось. Когда список станет нулевым можно закрыть соединение QFtp::close(), удалить экземпляр QFtp::deleateLater() и завершить поток, т.е. сделать тот самый выход из exec() - quit()/exit(0).
Автор: Константин 18.2.2009, 16:35
по всей видимости, тут ещё имеют место проблемы с пониманием асинхронное работы сокетов кутэ.
Цитата
void MyThread::run()
{
QFtp ftp = new QFtp;
ftp->connectToHost("ftp.trolltech.com");
ftp->login();
ftp->list();
......
ftp->close();
exec();
}
так делать не нужно. тем более, что ftp будет создан в родительском потоке. вызывай exec(), а всю работу с фтп вынеси из run()
Автор: me 18.2.2009, 16:35
Цитата(SABROG @ 18.2.2009, 15:45)
Таким образом все слоты будут обрабатываться не до и не после exec, а во время.
Тоесть начиная с того как был вызван непосредственно exec и мы вошли в event loop и до тех пока не вызовем завершить поток,
Цитата(SABROG @ 18.2.2009, 15:45)
т.е. сделать тот самый выход из exec() - quit()/exit(0)
Большое спасибо!!! По этому поводу все вопросы в сущности и были)
Автор: me 18.2.2009, 16:42
Цитата(Константин @ 18.2.2009, 16:35)
по всей видимости, тут ещё имеют место проблемы с пониманием асинхронное работы сокетов кутэ.
Так оно и есть...
Цитата(Константин @ 18.2.2009, 16:35)
тем более, что ftp будет создан в родительском потоке
Я там торопился... не указатель а просто
QFtp ftp;
Тоесть сделать так:
void MyThread::run()
{
QFtp ftp;
ftp.connectToHost("ftp.trolltech.com");
exec();
}
а дальше уже ловить успешное подключение и действовать дальше ?
Автор: me 18.2.2009, 16:43
не забыв конечнео же сделать connectы всё какие нужно...
Автор: me 18.2.2009, 16:48
Цитата
Классы QHttp и QFtp предоставляют поддержку протоколов HTTP и FTP на клиентской стороне. Так как эти два протокола используются для решении похожих задач, классы QHttp и QFtp имеют много общих особенностей:
* Неблокирующее поведение. QHttp и QFtp асинхронны. Вы можете отправить ряд команд (также называемые "запросы" для HTTP). Команды выполнятся позже, когда управление вернется к циклу событий Qt.
* ID команд. Каждая команда имеет свой уникальный номер ID, который используется для слежения за выполнением команд. Например, QFtp передает сигналы commandStarted() и commandFinished() с ID команды для каждой выполняемой команды. У QHttp тоже имеются подобные сигналы requestStarted() и requestFinished().
* Индикаторы процесса передачи данных. QHttp и QFtp посылают сигналы при передаче данных (QFtp::dataTransferProgress(), QHttp::dataReadProgress() и QHttp::dataSendProgress()). Вы можете соединить данные сигналы, например, с QProgressBar::setProgress() или QProgressDialog::setProgress().
* Поддержка QIODevice. Оба класса поддерживают загрузку в и скачивание из QIODevice, дополнительно к API, основанному на QByteArray.
Есть два основных способа использования QHttp и QFtp. Самый обычный способ состоит в том, чтобы отслеживать ID команд и следить за выполнением каждой команды, соединившись с соответствующим сигналом. Другой способ состоит в том, чтобы запланировать все команды сразу и соединиться только с сигналом done(), который посылается, когда все команды были выполнены. Первый способ требует большего количества работы, но дает больший контроль над выполнением конкретных задач и позволяет вводить дополнительные команды, основанные на результате предыдущих. Что позволяет обеспечить пользователю детализированную обратную связь.
Чёрт, как-то забыл совсем что есть общая статья про сетевое взаиможействие и совсем упустил из виду что тамCC написанно.
Автор: Константин 18.2.2009, 16:49
Цитата(Гость_me_* @ 18.2.2009, 16:42)
а дальше уже ловить успешное подключение и действовать дальше ?
не забыв конечнео же сделать connectы всё какие нужно...
именно. в таком случае при невозможности соединиться (connectToHost) можно выйти и завершить поток, даже не запуская его петлю событий (т.к. вряд ли тебе нужен поток с неподключённым фтп-клиентом)
Автор: me 18.2.2009, 17:41
Всем спасибо! Тему я думаю на этом закрыть можно..
Автор: Константин 18.2.2009, 18:02
Цитата(Гость_me_* @ 18.2.2009, 17:41)
Всем спасибо!
жми соответствующую кнопочку
Автор: me 2.3.2009, 16:35
В продолжение этой темы.. как-то всё не доходили руки сесть за всё это, но как наконец-то сел начались новые непонимания..
FtpThread.h:
#include "FtpThread.h"
void FtpThread::run()
{
// определение и инициализация соединения
ftpConnection = new QFtp();
connect(ftpConnection, SIGNAL(commandFinished(int, bool)), this, SLOT(commandManage(int, bool)), Qt::DirectConnection);
ftpConnection->connectToHost(this->server);
qDebug() << "Thread" << QThread::currentThreadId();
exec();
}
FtpThread::FtpThread(QString *server, QString *login, QString *pass)
{
if (server == NULL)
; // пока без этого..
else
this->server = *server;
if (login == NULL)
this->login="anonymous";
else
this->login = *login;
if (pass == NULL)
this->pass = "password";
else
this->pass = *pass;
ftpConnection = NULL;
}
void FtpThread::commandManage(int command, bool isError)
{
if (isError == true)
{
qDebug() << "Error:" << command << ftpConnection->errorString();
return;
}
else
{
switch(command)
{
case 1: // connectToHost прошёл успешно.. логинимся
qDebug() << "Loging 1" << QThread::currentThreadId();
ftpConnection->login(this->login, this->pass);
return;
case 2:
qDebug() << "Logged In 2" << QThread::currentThreadId();
ftpConnection->close();
return;
case 3:
qDebug() << "Closed 3" << QThread::currentThreadId();
quit();
return;
default:
qDebug() << "OPPA!!" << command << QThread::currentThreadId();
return;
}
}
}
FtpThread.cpp
#ifndef FTPTHREAD_H_
#define FTPTHREAD_H_
#include <QtNetwork>
#include <QCoreApplication>
#include <QQueue>
// Тип загрузки. С сервера или на сервер.
enum LoadDirection {TO_THE_SERVER, FROM_THE_SERVER};
class FtpThread : public QThread
{
Q_OBJECT
public:
FtpThread(QString *server = NULL , QString *name = NULL, QString *pass = NULL);
public slots:
void commandManage(int, bool);
protected:
void run();
private:
// настройки подключения к серверу
QString server;
QString login;
QString pass;
LoadDirection load;
// (up/down)load settings
QString from;
QString to;
// Ftp соединение.
QFtp *ftpConnection;
// Задания
QQueue<QString> tasks;
};
#endif /* FTPTHREAD_H_ */
main.cpp
#include <QtCore>
#include <QtNetwork>
#include <QCoreApplication>
#include "FtpThread.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString host = "ftp.freebsd.org";
QString user = "anonymous";
QString pass = "pass";
QString dir = "/pub/FreeBSD/";
qDebug() << "Main thread" << QThread::currentThreadId();
FtpThread ftpWorker(&host);
FtpThread ftpWorker2(&host);
ftpWorker.start();
ftpWorker2.start();
//ftpWorker.wait();
// qDebug() << QThread::currentThreadId() << "After Wait";
return a.exec();
}
Вывод в консоль=(((
Main thread 3071719120
Thread 3070524304 QFtp(0x80564b0)
Thread 3053738896 QFtp(0x805d8c0)
Loging 1 3070524304 QFtp(0x80564b0)
Logged In 2 3053738896 QFtp(0x805d8c0)
OPPA!! 4 3053738896 QFtp(0x805d8c0)
Closed 3 3070524304 QFtp(0x80564b0)
Толи на оба QFtp даётся общая последовательность ID команд.. толи не знаю. Если это так то очевидно лучше использовать
currentCommand () + слоты на запуск команды и конец команды...? Что я делаю не так??
Автор: SABROG 2.3.2009, 17:47
Рекомендация по поводу использования NULL: http://www.prog.org.ru/topic_7458_0.html
Автор: me 2.3.2009, 17:49
Забыл вырезать из вывода QFtp(0x80564b0) - эт я решил в какой=то момент посмотреть на адреса объектов QFtp для каждого потока...
Автор: me 2.3.2009, 18:20
Последовательность ID действительно одна для всех объектов QFtp ((
Прийдётся через currentThreadId и дальнейший анализ что за команда выполняется.. меня вот кстати всегда мучал вопрос а не может ли случиться так,
что если мы ловим сигнал от commandStarted и в слоте вызываем currentThreadId не может ли она хотя бы теоретически вернуть другую команду...
Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)