为什么QThread::finished信号没有被发出?

3
我创建了自己的 TestService 类,它在单独的 QThread 上运行,但当 MainLoop 终止时,QThread::finished 信号不会被发出。我看到了一个类似的问题,但那里的问题略有不同,因为 OP 过载了 QThread,而我只是将我的类移动到线程中。
请注意,我没有过载 QThread 类,我只是根据这个示例过载了 QObjecthttp://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ 这是我的 TestService 类:
#include <QObject>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <iostream>

using namespace std;
class TestService: public QObject
{
    Q_OBJECT;
private:
    volatile int _count;
    QWaitCondition _monitor;
    QMutex _mutex;
    QThread* _thread;

public:
    TestService(int numSeconds)
    {
        _count = numSeconds;
        _thread = NULL;
        cout << "TestService()" << endl;
    }

    virtual ~TestService()
    {
        cout << "~TestService()" << endl;
    }

    void Start()
    {
        QMutexLocker locker(&_mutex);
        if(_thread == NULL)
        {
            _thread = new QThread;

            // Move this service to a new thread
            this->moveToThread(_thread);

            // The main loop will be executed when the thread
            // signals that it has started.
            connect(_thread, SIGNAL(started()), this, SLOT(MainLoop()));

            // Make sure that we notify ourselves when the thread
            // is finished in order to correctly clean-up the thread.
            connect(_thread, SIGNAL(finished()), this, SLOT(OnFinished()));

            // The thread will quit when the sercives
            // signals that it's finished.
            connect(this, SIGNAL(Finished()), _thread, SLOT(quit()));

            // The thread will be scheduled for deletion when the 
            // service signals that it's finished
            connect(this, SIGNAL(Finished()), _thread, SLOT(deleteLater()));

            // Start the thread
            _thread->start();
        }
    }

    void Stop()
    {
        _count = 0;
        _monitor.wakeAll();
    }
private slots:
    void MainLoop()
    {
        cout << "MainLoop() Entered" << endl;
        while(_count > 0)
        {
            cout << "T minus " << _count << " seconds." << endl;

            QMutexLocker locker(&_mutex);
            _monitor.wait(&_mutex, 1000);
            _count--;
        }
        cout << "MainLoop() Finished" << endl;
        emit Finished();
    }

    virtual void OnFinished()
    {
        cout << "OnFinished()" << endl;
    }
signals:
    void Finished();
};

这里是测试代码:
void ServiceTest()
{
    cout << "Press q to quit." << endl;
    cout << "Press s to start." << endl;
    cout << "Press t to stop." << endl;
    QSharedPointer<TestService> testService(new TestService(10));
    char in = 'a';
    while( in != 'q' )
    {
        switch(tolower(in))
        {
        case 's':
            testService->Start();
            break;
        case 't':
            testService->Stop();
            break;
        default:
            break;
        }
        cin.get(in);
        in = tolower(in);
    }
}

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

    ServiceTest();

    QTimer::singleShot(0, &a, SLOT(quit()));

    return a.exec();
}

输出结果为:
Press q to quit.
Press s to start.
Press t to stop.
TestService()
s
MainLoop() Entered
T minus 10 seconds.
T minus 9 seconds.
T minus 8 seconds.
t
MainLoop() Finished
q
~TestService()
Press any key to continue . . .

能有人解释一下为什么 finished 没有被触发以及如何修复它吗?
1个回答

9

信号finished()被发射,但你没有捕获它。

这里:

connect(_thread, SIGNAL(finished()), this, SLOT(OnFinished()));

Qt::QueuedConnection被使用,因为_threadthis(服务)在不同的线程中。

当发出finished()信号时,_thread的事件循环已经执行完毕,因此信号将不会传递到槽。

您可以显式地使用Qt::DirectConnection

编辑:

QThread的工作方式如下:

QThread::start()
{
    emit started();
    run();
    emit finished();
}

QThread::run()
{
     eventloop->exec();
}

因此,在发出finished信号时,事件循环已经停止执行。当您将service移动到_thread时,服务的事件循环是_thread的事件循环。

请注意,QObject本身没有自己的事件循环。对话框、线程和应用程序创建事件循环。


实际上,在您的简单情况下,我建议只使用QtConcurent::run,因为您在新线程中不执行实际事件处理,而只运行单个函数。


1
谢谢您的回答,但是我的情况略有不同,因为我的“TestService”没有重载“QThread”,它只重载了“QObject”。我基于的代码示例在这里:http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/ 关于“QtConcurrent::run”,我希望不要在这个示例中使用boost,因为我严格遵循只使用Qt库的原则。 - Kiril
@Lirik,抱歉我误读了这篇文章:“将_it_移动到线程”被解释为“将_thread_移动到线程”(这不比子类化更好)。我编辑了我的答案。 - Lol4t0
1
@Lirik,另外请注意,QtConcurent::run 是完全基于 Qt 的,与 boost 没有任何关系。 - Lol4t0
2
我刚刚注意到QtCurrent :: run中的示例使用了boost :: bind感谢更新的答案:当您说发出finished()信号时,_thread的事件循环已经完成执行时,我仍然困惑的是您的意思,...我不是要在 _thread的事件循环中处理它,而是在 TestService的事件循环中处理它。如果我理解正确,由于服务尚未被销毁,因此TestService事件循环仍应处于活动状态。 - Kiril
1
算了,我刚意识到QThread::finished()信号的接收者是一个没有事件循环的线程上的对象,所以他的问题是槽从未被触发,而不是信号从未被发射。 - Phlucious
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接