由于QCoreApplication事件循环,QThread永不退出

时间:2015-04-15 02:44:43

标签: c++ multithreading qt qthread

问题

所以我有一个CommandRetriever类,它包含一些命令,而在不同的线程上执行这些命令。

class CommandRetriever
{
    public:
        CommandRetriever();
        ~CommandRetriever();

        void addCommand( QString, Command* );
        void executeCommands();

    private:
        QMap<QString, Command*> m_commands;
};

addCommand会在m_commands中添加新条目。但是,这是有问题的功能。

void CommandRetriever::executeCommands()
{
    for( auto iter : m_commands.keys() )
    {
        Command *cmd = m_commands.value( iter ); 

        // Command, password //
        RemoteConnection *rcon = new RemoteConnection( cmd, "" );

        QThread *thread = new QThread();
        rcon->moveToThread( thread );
        QObject::connect( thread, SIGNAL( started() ), rcon, SLOT( doAsync() ) );
        QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( quit() ) );
        QObject::connect( rcon, SIGNAL( finished() ), rcon, SLOT( deleteLater() ) );
        QObject::connect( thread, SIGNAL( finished() ), thread, SLOT( deleteLater() ) );
        thread->start();
    }
}

RemoteConnection是我的工作线程。 doAsync()插槽处理需要处理的内容。

出于某种原因,我的doAsync()对象的rcon函数将被调用,但线程永远不会退出...我的主类看起来像这样:

int main( int argc, char *argv[] )
{           
    QCoreApplication app( argc, argv );     
    CommandRetriever cr( addr, port );

    //cr.addCommand() ...

    cr.executeCommands();

    return app.exec();
}

我的工作对象(rcon)将输出他们应该的内容。但是,即使我的worker对象发出finished()信号,线程仍然存在,我的应用程序永远不会完成。我之前连接了这样的信号:

QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( deleteLater() ) );

但后来我收到了错误:QThread: Destroyed while thread is still running和一个段错误。

我最近发布了一个与我的主题有相关问题的问题,解决方案是调用thread->wait(),但是,我看到如果我有一个事件循环,则不需要thread->wait(),并不是一个合适的解决方案。使用当前代码,我没有错误,但我的应用程序永远运行。我猜测app.exec()事件循环是无限运行的,如果我是对的,我怎么能正确关闭所有东西呢?


解决方案

正如thuga所说:

  

你需要告诉你的QCoreApplication退出,否则它永远不会从exec函数返回。

这可以通过连接我的工作者对象的finished()信号来轻松实现,以使我的应用程序退出:

QObject::connect( rcon, SIGNAL( finished() ), app, SLOT( quit() ) );

但是,如果我有一个窗口或其他一些GUI元素,这就更有意义,这样我就能明确知道何时需要关闭我的程序(例如用户关闭主窗口)。

因为我正在运行多个线程并且因为我的应用程序只是一个控制台应用程序,所以将我的线程的finished()信号连接到QCoreApplication的{​​{1}}插槽是没有意义的。所以我最终使用的是quit()

QThreadPool

应该注意RemoteConnection类扩展void CommandRetriever::executeCommands() { // Quick iterate through the command map // for( auto iter : m_commands.keys() ) { Command *cmd = m_commands.value( iter ); // Command, password; start a new thread for a remote connection. // RemoteConnection *rcon = new RemoteConnection( cmd, "" ); QThreadPool::globalInstance()->start( rcon ); } }

要等待线程完成,我打电话:

QRunnable

这将阻塞主线程,直到所有线程完成执行,这对我的控制台应用程序更有意义。它还使代码变得更加清洁。

我还删除了main.cpp中的QThreadPool::globalInstance()->waitForDone(); ,因为我不再需要app.exec()提供的事件循环。

我学到了什么? QCoreApplication不是实现多线程的唯一方法。在我的情况下,使用QThread更有意义,因为我不需要QThreadPool的信号/插槽。此外,如果不需要,则不应使用QThread事件循环。大多数控制台应用程序都属于这一类。


来源

http://doc.qt.io/qt-5/qrunnable.html

http://doc.qt.io/qt-4.8/qthreadpool.html

3 个答案:

答案 0 :(得分:3)

如果您没有告诉您的QCoreApplication对象退出,它将永远不会离开exec()功能。

您可以直接拨打QCoreApplication::quit()插槽,也可以连接信号。

...
connect(thread, SIGNAL(finished()), qApp, SLOT(quit()));

qApp是一个引用唯一应用程序对象的全局指针。这与调用QCoreApplication::instance()相同。

答案 1 :(得分:1)

尝试将finished()的{​​{1}}信号连接到RemoteConnection的{​​{1}}广告位,而不是terminate()广告位。

QThread

根据操作系统的调度策略,线程可能会立即终止也可能不会立即终止。在quit()之后使用QObject::connect( rcon, SIGNAL( finished() ), thread, SLOT( terminate() ) ); ,以确定。

答案 2 :(得分:0)

只需在你的dll类“CommandRetriever”的初始化程序中创建一个新的QCoreApplication对象,然后调用:processEvents,一次。如下:

//Create the QCoreApplication object
int argc = 1;
char * argv[] = {"my.dll", NULL};
dllEventHandler = new QCoreApplication(argc, argv);
//Start: process events
dllEventHandler->processEvents();

就是这样。您不需要main函数或QCoreApplication .exec()。