为了在一个单独的线程中创建一个Qt事件循环,从一个用Java编写的主应用程序调用的DLL中,我根据我读过here的建议完成了以下操作,哪个效果很好:
// 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的来龙去脉,以便为此提出解决方案?或者我设计的方式存在根本缺陷吗?
答案 0 :(得分: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仍处于清理自身的过程中,那么可能存在冲突的风险非常小。我不认为这可能发生在我使用它的环境中。