2014-06-21 302 views
1

爲了在一個單獨的線程中創建一個Qt事件循環,在由Java編寫的主應用程序調用的DLL中創建一個Qt事件循環,我做了以下工作,根據建議,我讀here,它的工作原理相當不錯:在QThread中的QCoreApplication上調用quit()時發生錯誤

// Define a global namespace. We need to do this because the parameters 
// passed to QCoreApplication must have a lifetime exceeding that of the 
// QCoreApplication object 
namespace ToolThreadGlobal 
{ 
    static int argc = 1; 
    static char * argv[] = { "MyVirtualMainApplication.exe", NULL }; 
    static QCoreApplication *coreApp = nullptr; 
    static ToolThread *toolThread = nullptr; 
}; 

//! The ToolThread class differs from a standard QThread only 
//! in its run() method 
class ToolThread : public QThread 
{ 
    //! Override QThread's run() method so that it calls 
    //! the QCoreApplication's exec() method rather than 
    //! the QThread's own 
    void run() { ToolThreadGlobal::coreApp -> exec(); } 
}; 

class ThreadStarter : public QObject 
{ 
    Q_OBJECT 

public: 
    //! Constructor 
    ThreadStarter() 
    { 
     // Set up the tool thread: 
     if (!ToolThreadGlobal::toolThread) 
     { 
      ToolThreadGlobal::toolThread = new ToolThread(); 
      connect(ToolThreadGlobal::toolThread, &ToolThread::started, 
        this, &ThreadStarter::createApplication, Qt::DirectConnection); 
        // The thread's 'started' event is emitted after the thread 
        // is started but before its run() method is invoked. By 
        // arranging for the createApplication subroutine to be 
        // called before run(), we ensure that the required 
        // QCoreApplication object is instantiated *within the 
        // thread* before ToolThread's customised run() method 
        // calls the application's exec() command. 
      ToolThreadGlobal::toolThread->start(); 
     } 
    } 

    //! Destructor 
    ~ThreadStarter() 
    { 
     // Ensure that the thread and the QCoreApplication are cleanly 
     // shut down: 
     ToolThreadGlobal::coreApp -> quit(); 
     delete ToolThreadGlobal::coreApp; 
     ToolThreadGlobal::coreApp = nullptr; 
     delete ToolThreadGlobal::toolThread; 
     ToolThreadGlobal::toolThread = nullptr; 
    } 

    //! Function to return a pointer to the actual tool thread: 
    ToolThread* getThread() const { return ToolThreadGlobal::toolThread; } 

private: 
    //! Create the QCoreApplication that will provide the tool 
    //! thread's event loop 
    /*! This subroutine is intended to be called from the tool 
     thread itself as soon as the thread starts up. 
    */ 
    void createApplication() 
    { 
     // Start the QCoreApplication event loop, so long as no 
     // other Qt event loop is already running 
     if (QCoreApplication::instance() == NULL) 
     { 
      ToolThreadGlobal::coreApp = new QCoreApplication(ToolThreadGlobal::argc, 
                  ToolThreadGlobal::argv); 
     } 
    } 
}; 

要使用這一點,從主要的Java應用程序的線程調用的子程序只需要創建一個ThreadStarter對象 將自動與創建ToolThread在其中運行的QCoreApplication:

itsThreadStarter = new ThreadStarter(); 
itsToolThread = itsThreadStarter -> getThread(); 

然後我們就可以實例化一個QObject類以通常的方式,將其移動到線程異步和使用調用它的方法QMetaObject :: invokeMethod中:

itsWorker = new Worker(); 
itsWorker -> moveToThread(itsToolThread); 

QMetaObject::invokeMethod(itsWorker, 「doSomethingInteresting」); 

當我們做,我們只是刪除ThreadStarter對象一切都很好地清理。除了令人討厭的消息說

WARNING: QApplication was not created in the main() thread 

在啓動時,它似乎滿足我所有的要求。

除...(以及最後,這是我的問題)。

有時 - 並且沒有任何我目前能夠辨別的模式 - 在關機過程中出現錯誤。它通常發生在行

 delete ToolThreadGlobal::coreApp; 

,但有時在該行

ToolThreadGlobal::coreApp -> exec(); 

(當然,在線程的run執行()方法並不會返回,直到ToolThreadGlobal :: coreApp後 - > quit();已經完全執行)。

錯誤消息通常是一個簡單的訪問衝突;有時它是一個相當更復雜:

ASSERT failure in QObjectPrivate::deleteChildren(): "isDeletingChildren already set, did this function recurse?", file ..\qtbase\src\corelib\kernel\qobject.cpp, line 1927 

我以爲這是因爲,一旦我發出退出()命令將QCoreApplication,我要等待一些時間,它在刪除之前,正確關閉事件循環它 - 就像人們通常在刪除它之前通常調用quit()然後在普通的QThread上等待()。然而,QCoreApplication似乎沒有等效的wait()命令,並且我無法實現QTimer來強制延遲,因爲一旦我使用quit()關閉了事件循環,它就無法工作。因此,我無所適從。我有一個暗示,因爲QCoreApplication是一個QObject,我可以調用它的deleteLater()方法,但我看不到應該從哪裏調用它。

是否有專家瞭解QCoreApplication和QThread的來龍去脈並提出解決方案?或者我設計的方式存在根本性缺陷?

+0

你想實現什麼?停止整個過程或刪除Qt部分?退出()停止整個過程,我不明白爲什麼你會想要做任何*調用後。我將假設quit()後的任何刪除操作都是多餘的。 「正在刪除孩子已經設置」聽起來就像那樣。 – ypnos

+0

我想要的是在Java應用程序的其餘部分繼續運行時停止單獨的Qt線程,同時讓事情處於可以在稍後啓動新的Qt線程的狀態。換句話說,我想要讓ToolThreadGlobal命名空間中的兩個指針再次爲空,而沒有任何錯誤消息一直被拋出。 –

回答

0

這似乎適用於我...

首先,我添加了一個靜態的 '清理' 功能,我的全局命名空間:

namespace ToolThreadGlobal 
{ 
    static int argc = 1; 
    static char * argv[] = { "MyVirtualMainApplication.exe", NULL }; 
    static QCoreApplication *coreApp = nullptr; 
    static ToolThread *toolThread = nullptr; 
    static void cleanup() { coreApp->deleteLater(); coreApp = nullptr; } 
}; 

然後,從我的ThreadStarter :: createApplication插槽我的QCoreApplication的aboutToQuit信號連接到它:

void createApplication() 
    { 
     // Start the QCoreApplication event loop, so long as no other Qt event loop 
     // is already running 
     if (QCoreApplication::instance() == NULL) 
     { 
      ToolThreadGlobal::coreApp = new QCoreApplication(ToolThreadGlobal::argc, 
                  ToolThreadGlobal::argv); 
      connect(ToolThreadGlobal::coreApp, &QCoreApplication::aboutToQuit, 
        ToolThreadGlobal::cleanup); 
     } 
    } 

然後,'ThreadStarter'析構函數減少到只有五行(包括Qthread :: quit()和QThread :: wait()的調用,這應該是第一次出現):

~ThreadStarter() 
    { 
     // Ensure that the thread and the QCoreApplication are cleanly shut down: 
     ToolThreadGlobal::coreApp -> quit(); 
     ToolThreadGlobal::toolThread -> quit(); 
     ToolThreadGlobal::toolThread -> wait(); 
     delete ToolThreadGlobal::toolThread; 
     ToolThreadGlobal::toolThread = nullptr; 
    } 

當ThreadStarter析構函數調用QCoreApplication :: quit()時,QCoreApplication在其事件循環仍在運行時調用清除函數。這會安排QCoreApplication在其準備好之後自行刪除,同時將全局指針重置爲NULL,以便應用程序的其餘部分知道可以在需要時實例化新的QCoreApplication。

如果主應用程序立即創建一個新的QCoreApplication並嘗試在其上運行exec(),而舊的QCoreApplication仍在清理自己的過程中,則可能會有衝突的風險很小。我不認爲這可能發生在我使用它的環境中。

相關問題