[英]Error when calling quit() on a QCoreApplication within a QThread
為了從一個用Java編寫的主應用程序調用的DLL中的一個單獨線程中創建Qt事件循環,我根據在這里讀到的建議做了以下工作,該工作很好:
// 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對象,該對象將自動創建一個在其中運行QCoreApplication的ToolThread:
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()命令,我應該等待一會兒,以便它在關閉事件循環之前將其正確關閉,就像通常調用quit()然后等待()在普通QThread上刪除。 但是,QCoreApplication似乎不具有wait()命令的等效功能,並且我無法實現QTimer來強制延遲,因為一旦用quit()關閉事件循環,它就將無法工作。 因此,我無所適從。 我有一個暗示,因為QCoreApplication是QObject,所以我可以調用它的deleteLater()方法,但是看不到應該從哪里調用它。
有沒有一位專家足夠了解QCoreApplication和QThread的來龍去脈,提出解決方案? 還是我設計此方法的根本缺陷?
這似乎對我有用...
首先,我向全局名稱空間添加了一個靜態的“清理”函數:
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會在事件循環仍在運行時調用cleanup函數。 這樣可以安排QCoreApplication在狀態良好並准備就緒后刪除自身,同時將全局指針重置為NULL,以便應用程序的其余部分知道可以在需要時實例化新的QCoreApplication。
我想如果主應用程序立即創建一個新的QCoreApplication並嘗試在舊的QCoreApplication仍在清理自身的過程中嘗試在其上運行exec(),那么這可能會產生沖突。 我認為在我使用它的情況下不太可能發生這種情況。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.