LINUX.ORG.RU

[Qt] Потоки со своим eventloop'ом


0

3

Пусть есть вот такие часы:

#ifndef _ASTIME_H_53828_
#define _ASTIME_H_53828_

#include <QtGui>

class AsTime : public QLabel
{
    Q_OBJECT

public:
    AsTime( QWidget* parent = 0 );

public slots:
    void update();
};

#endif /* _ASTIME_H_53828_ */
#include "astime.h"

void clockWork( QObject* parent )
{
    QEventLoop loop;

        QTimer timer;
        timer.setInterval( 500 );
        timer.start();
        QObject::connect( &timer, SIGNAL( timeout() ), parent, SLOT( update() ) );
        QObject::connect( parent, SIGNAL( destroyed() ), &loop, SLOT( quit() ) );
//        QObject::connect( QCoreApplication::instance(), SIGNAL( aboutToQuit() ), &loop, SLOT( quit() ) );

    loop.exec();
}

AsTime::AsTime( QWidget* parent ) : QLabel( QTime::currentTime().toString(), parent )
{
    this->setAlignment( Qt::AlignCenter );
}

void AsTime::update()
{
    this->setText( QTime::currentTime().toString() );
}

В main'е запускается главная петля событий, создаётся и отображается экземпляр AsTime. Всё работает. При закрытие окна приложение либо завершается, либо (как правило) нет. Если раскомментировать строчку с выходом из локальной петли событий по aboutToQuit сигналу, то приложение как правило завершается после закрытия окна, но иногда этого не делает. Я кажется что-то не понимаю в передаче сигналов между потоками и что-то делаю не так. Собственно три вопроса: что же я делаю не так? почему результат зависит от фазы сириуса? существует ли какой-то дебагер для ковыряния в Qt'шном рантайме (имеются в виду сигналы, слоты, события)?

★★★★★

Что-то не вижу я твоих «потоков» в коде.

mannaz ()

Покажи код с запуском отдельного потока.

что же я делаю не так?

Я не понял, зачем тебе отдельный QEventLoop в отдельной функции. Ты ее через QtConcurrent::run запускаешь?
Почему бы не пойти каноническим путем и не отнаследоваться от QThread?

#include "astime.h"

class ClockThread : public QThread {
public:
    ClockThread(QObject *parent)
        : QThread( parent ) 
    {
        QTimer *timer = new QTimer(this);
        connect( timer, SIGNAL( timeout() ), parent, SLOT( update() ) );
        timer->setInterval( 500 );
        timer->start();
    }
};


AsTime::AsTime( QWidget* parent )
    : QLabel( QTime::currentTime().toString(), parent )
    , m_thread(new ClockThread(this))
{
    this->setAlignment( Qt::AlignCenter );

    m_thread->start();
}

AsTime::~AsTime() {
    m_thread->quit();
    m_thread->wait(1000);
}

void AsTime::update()
{
    this->setText( QTime::currentTime().toString() );
}

anonymous ()

1. Приведи полный код с main'ом

2. Вызывай connect() до вызова timer.start().

anonymous ()
Ответ на: комментарий от anonymous

Код с таймером нужно перенести в run():

class ClockThread : public QThread {
public:
    ClockThread(QObject *parent) : QThread( parent ) {}
    
protected:
    void run() {
        QTimer timer;
        connect( &timer, SIGNAL( timeout() ), parent(), SLOT( update() ) );
        timer.setInterval( 500 );
        timer.start();
        exec();
    }
};

anonymous ()

омг. тебе надо protected timerEvent переопределить и оттудова вызывать апдейт. А в конструкторе сделать startTimer(500) и всё. потоки вообще тут не нужны, QTimer тоже не нужен. А QEventLoop и подавно, оставь его большим дядям.

mi_estas ()
Ответ на: комментарий от mi_estas
#pragma once

#include <QtGui>
#include<QTimerEvent>

class AsTime : public QLabel
{
    Q_OBJECT

public:
    AsTime( QWidget* parent = 0 );
protected:
    void timerEvent(QTimerEvent *);
};

#include"astime.h"

AsTime::AsTime(QWidget *p): QLabel(p)
{
    startTimer(500);
    setText("hello mazafackers! timer didnt make any ticks yet");
}

void AsTimer::timerEvent(QTimerEvent *)
{
    setText(QTime::currentTime().toString());
}

походу и конект не нужен тоже.

mi_estas ()
Ответ на: комментарий от anonymous

Потерял строчку. Функция с EventLoop'ом дёргается при помощи QtConcurrent::run из конструктора Label'а. Пойти каноническим путём я могу. Мне не понятно почему не работает этот.

KblCb ★★★★★ ()
Ответ на: комментарий от KblCb

Мне не понятно почему не работает этот.

Сигналы по сути создают события типа QMetaCallEvent, которые кладутся в очередь sender'ом с помощью postEvent (по крайней мере при queued connection), но так как кладутся они в очередь событий потока receiver'а, то и обрабатываются они уже в потоке receiver'а.
В твоем случае sender и receiver находятся в разных потоках.

Чтобы нужный слот вызвался событие должно пройти через QApplication::notify(), в котором есть такое условие:

 if (QApplicationPrivate::is_app_closing)
        return true;

При удачном стечении обстоятельств поток receiver'а успеет вызвать notify до того, как в ~QApplication() выполнится код:

    QApplicationPrivate::is_app_closing = true;
    QApplicationPrivate::is_app_running = false;
При неудачном - событие от сигнала aboutToQuit (или destroyed) проигнорируется, и исполнение застрянет в куске кода из ~QCoreApplication(), ожидая завершения loop.exec():
   // Synchronize and stop the global thread pool threads.
    QThreadPool *globalThreadPool = 0;
    QT_TRY {
        globalThreadPool = QThreadPool::globalInstance();
    } QT_CATCH (...) {
        // swallow the exception, since destructors shouldn't throw
    }

    if (globalThreadPool)
        globalThreadPool->waitForDone();

Не знаю, можно ли это считать багом кутэ или нет, но обойти можно, например, так:

int main(int argc, char *argv[])
{
    int ret;
    {
        Application a(argc, argv);

        AsTime w;
        w.show();

        QtConcurrent::run(clockWork, &w);

        ret = a.exec();

        QThreadPool::globalInstance()->waitForDone();
    }
    return ret;
}

anonymous ()
Ответ на: комментарий от anonymous
ret = a.exec(); // emit aboutToQuit() делается тут
QThreadPool::globalInstance()->waitForDone();
anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.