Russian Qt Forum

Qt => Общие вопросы => Тема начата: Unnamed_Hero от Июль 09, 2008, 09:49



Название: как правильно работать с QThread? (закачка файла в треде)
Отправлено: Unnamed_Hero от Июль 09, 2008, 09:49
Хочу понять, как правильно работать с тредами. Почитав книги по qt4, понял мало, там очень сухо всё описано.

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

Значит в треде должны быть свои сигналы и слоты.

В методе треда run() последней строчкой стоит exec()

Если стартовать тред с помощью start(), то он запускается и сразу же помирает, не скачав и байта (тред не ловит ни слотов, ни сигналов). Если запускать с помощью run(), то тред отрабатывает, но после отработки тред виснет, т.е. программа висит в ожидании. Если в программе  нажать на quit, то, видимо, тред грохается и программа продолжает работать. повторное нажатие на quit срабатывает нормально.

Вероятно, я не могу выйти из EventLoop, вызов quit(), exit(0), exit() - не помогают.

Ну, и главный номер программы -  мой ugly code:
тредовый .h
Код:
class get_xml : public QThread
{
Q_OBJECT
public:

get_xml (QObject *parent = 0,QStringList *_initList=0);
~get_xml ();


void run ();
private:
QHttp http;

QFile xmlfile;

QByteArray result;
QStringList* initList;

QString charfile;
private slots:
void readData();
void save2File();

signals:
void finita();

};

тредовый .cpp

Код:
get_xml::get_xml (QObject *parent,QStringList *_initList)
:initList (_initList)
{

}
void get_xml::run ()
{

qDebug ("getXML STARTED");

QHttpRequestHeader header ("GET", initList->at(1),1,1);

QByteArray opts;

connect (&http,SIGNAL (readyRead (QHttpResponseHeader)),this, SLOT (readData()));
connect (&http,SIGNAL (done (bool)),this,SLOT (save2File()));
header.setContentType("application/x-www-form-urlencoded");
header.setValue("Host", "bla-bla.com");
http.setHost ("bla-bla.com");
xmlfile.setFileName (initList->at(0)+initList->at(4)+QDir::separator()+file.fileName());
opts.append ("<ny cool opts>");
http.request (header,opts,0);
exec();


}

get_xml::~get_xml()
{

qDebug ("xml thread убилсо");

}

void get_xml::readData()
{

qDebug ("тяну-потяну XML...");
result.append (http.readAll());

void get_xml::save2File ()
{
disconnect (&http,0,0,0);
xmlfile.open (QIODevice::WriteOnly);
qDebug ()<<"downloading complete"
xmlfile.write (result);
xmlfile.close();

emit (finita());
}


В главном классе
в .h
Код:
get_xml *myCurrSkill;

в .cpp

Код:
myCurrSkill = new get_xml (this,&initList);
bool skillConn (false);
connect (myCurrSkill,SIGNAL (finita()),this,SLOT (charCSkillReady()));
//myCurrSkill->start();
myCurrSkill->run();

....................
.....................
void char_Sheet::charCSkillReady ()
{

disconnect (myCurrSkill,0,0,0);
myCurrSkill->quit();
myCurrSkill->deleteLater();
}



Название: Re: как правильно работать с QThread? (закачка файла в треде)
Отправлено: SASA от Июль 09, 2008, 10:17
Первое что бросается в глаза: для запуска надо вызывать QThread::start(). Если мы вызываем run(), никаких потоков не создаётся. Просто функция исполняется в вызывающем потоке.
О потоках неплохо написано у Бланшет. Живую ссылку на книгу можно поискать здесь:
http://prog.org.ru/forum/index.php/topic,765.0.html (http://prog.org.ru/forum/index.php/topic,765.0.html)


Название: Re: как правильно работать с QThread? (закачка файла в треде)
Отправлено: Unnamed_Hero от Июль 09, 2008, 10:33
А если вызывать через start(), как я уже и писал - то не создаётся EventLoop треда, т.е. слоты и сигналы просто не обрабатываются, следовательно файл не скачивается...


Название: Re: как правильно работать с QThread? (закачка файла в треде)
Отправлено: pastor от Июль 09, 2008, 14:45
А если вызывать через start(), как я уже и писал - то не создаётся EventLoop треда, т.е. слоты и сигналы просто не обрабатываются, следовательно файл не скачивается...

Вы противоречите ассистанту и опыту людей. При возове start() вызываеться метод run() и так было и есть сейчас.

Цитировать
void QThread::run ()   [virtual protected]
The starting point for the thread. After calling start(), the newly created thread calls this function. The default implementation simply calls exec().


Название: Re: как правильно работать с QThread? (закачка файла в треде)
Отправлено: Alex03 от Июль 10, 2008, 07:52
Unnamed_Hero у Вас проблема в следующем:
QHttp http - член класса потомка от QThread, который создаётся в главном потоке приложения, соответственно http "принадлежит" главному потоку. Далее Вы его сигналы коннектите к этому потомку от QThread, который тоже в главном потоке.
Соответственно вызову exec() который в run просто нечего обрабатывать, у порождённого потока нет соответствующих объектов.
Т.е. если Вы перенесёте QHttp http например, на стек в run() то всё начнёт работать...
НО! простой connect() между http и слотами потомка QThread будет "межпоточный", и следовательно сама обработка будет в главном потоке, что не соответствует задуманной логике. Поэтому такие connect-ы надо делать с Qt::DirectConnection.

P.S. Иногда можно не мудрить с потоками а всё делать в главном потоке, для работы с сокетами и т.д. в Qt для этого всё есть. Иначе надо хорошо представлять как всё это работает, использовать механизмы межпоточной синхронизации и т.д., ибо в противном случае трудноотлавливаемые грабельки будут вылазить очень долго.


Название: Re: как правильно работать с QThread? (закачка файла в треде)
Отправлено: Unnamed_Hero от Июль 10, 2008, 09:53
спасибо за разъяснение.

В итоге сделал такую реализацию, которая даже работает:

в *.h треда заменил QHttp http на QHttp *_http;

В стеке run() объявил QHttp http, сделал  _http = &http;

и вне run () общался c http через _http->

В главном классе myCurrSkill->start();myCurrSkill->wait();


Название: Re: как правильно работать с QThread? (закачка файла в треде)
Отправлено: Alex03 от Июль 10, 2008, 12:54
в *.h треда заменил QHttp http на QHttp *_http;
В стеке run() объявил QHttp http, сделал  _http = &http;
Можно было и new....
В главном классе myCurrSkill->start();myCurrSkill->wait();
Если wait() то какой смысл в отдельном потоке?

Про коннекты то поняли?
readyRead() и done() надо коннектить с Qt::DirectConnection
а finita() с Qt::QueuedConnection



Название: Re: как правильно работать с QThread? (закачка файла в треде)
Отправлено: Unnamed_Hero от Июль 10, 2008, 15:02
wait() это пока что для отладки и тестов. и для экспериментов.

Про коннекты тоже понятно, спасибо. Так и было сделано, просто в предыдущем сообщении этого я не указал.