c++ - 由于 QCoreApplication 事件循环,QThread 永远不会退出

标签 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() 插槽处理需要处理的内容。

由于某种原因,我的 rcon 对象的 doAsync() 函数将被调用,但线程永远不会退出...我的主类如下所示:

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

    //cr.addCommand() ...

    cr.executeCommands();

    return app.exec();
}

我的工作对象 (rcon) 将输出它们应该输出的内容。然而,即使我的工作对象发出一个 finished() 信号,线程仍然存在,我的应用程序永远不会结束。我之前连接了这样的信号:

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

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

我最近发布了一个与我的线程相关的问题,解决方案是调用 thread->wait(),但是,我看到如果我有一个事件循环,thread->wait() 不需要,也不是合适的解决方案。使用当前代码,我没有收到任何错误,但我的应用程序会永远运行。我猜 app.exec() 事件循环正在无限运行,如果我是对的,我怎样才能正确关闭一切?


解决方案

正如暴徒所说:

You need to tell your QCoreApplication to quit, otherwise it will never return from the exec function.

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

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

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

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

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 );
    }
}

需要注意的是 RemoteConnection 类扩展了 QRunnable

为了等待线程完成,我调用:

QThreadPool::globalInstance()->waitForDone();

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

我还删除了 main.cpp 中的 app.exec(),因为我不再需要 QCoreApplication 提供的事件循环。

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


来源

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

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

最佳答案

如果你不告诉你的QCoreApplication对象退出,它永远不会离开exec()函数。

您可以直接调用 QCoreApplication::quit() 插槽,或将信号连接到它。

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

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

关于c++ - 由于 QCoreApplication 事件循环,QThread 永远不会退出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29640753/

相关文章:

java - 没有 happens-before 的安全发布?除了 final 还有什么?

c++ - 如何将 QSerialPort 模块添加到 CMake 中?

c++ - 运行前或运行后清理/重置

c++ - 根据硬件并发度将任务均分给线程

c++ - 为什么 strncpy 被标记为不安全?

java - java中的线程争用

c++ - 将 CfbsBitmap 格式转换为 JPG

c++ - Qt中删除QStringList

c++ - 您如何打包GCC进行分发?

具有二进制数据的 C++ fstream << 和 >> 运算符