簡體   English   中英

由於QCoreApplication事件循環,QThread永不退出

[英]QThread never quits due to QCoreApplication event loop

問題

所以我有一個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 )將輸出他們應該的東西。 但是,即使我的worker對象發出一個finished()信號,該線程仍然存在,我的應用程序永遠不會完成。 我之前連接過這樣的信號:

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

但后來我得到了錯誤: QThread: Destroyed while thread is still running出現故障並且出現seg故障。

我最近發布了一個與我的線程有相關問題的問題,解決方案是調用thread->wait() ,但是,我看到如果我有一個事件循環,則不需要thread->wait() ,並且不是一個合適的解決方案。 使用當前代碼,我沒有錯誤,但我的應用程序永遠運行。 我猜app.exec()事件循環是無限運行的,如果我是對的,我怎么能正確關閉所有東西?


正如thuga所說:

您需要告訴您的QCoreApplication退出,否則它將永遠不會從exec函數返回。

這可以通過連接我的worker對象的finished()信號來輕松實現,以使我的應用程序退出:

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

但是,如果我有一個窗口或一些其他GUI元素,這更有意義,這樣我就能明確知道何時需要關閉我的程序(例如用戶關閉主窗口)。

因為我運行多個線程並且因為我的應用程序只是一個控制台應用程序,所以將我的線程的finished()信號連接到我的QCoreApplicationquit()插槽是沒有意義的。 所以我最終使用的是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()

嘗試將RemoteConnectionfinished()信號連接到QThreadterminate()插槽而不是quit()插槽。

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

根據操作系統的調度策略,線程可能會立即終止也可能不會立即終止。 terminate() QThread::wait()之后使用QThread::wait() terminate() ,以確保。

只需在你的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()。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM