在QThread中的QCoreApplication上调用quit()时出错
Error when calling quit() on a QCoreApplication within a QThread
为了在一个单独的线程中创建一个Qt事件循环,从一个由Java编写的主应用程序调用的DLL中,我做了以下工作,基于我在这里读到的一个建议,它工作得相当好:
// 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 ..qtbasesrccorelibkernelqobject.cpp, line 1927
我认为这是因为,一旦我发出quit()命令给QCoreApplication,我应该等待一段时间,让它在删除它之前正确地关闭事件循环-就像人们通常在删除它之前调用quit(),然后在普通的QThread上等待()一样。然而,QCoreApplication似乎没有相当于wait()的命令,而且我不能实现QTimer来强制延迟,因为一旦我用quit()关闭了事件循环,它就不起作用了。因此,我不知道该怎么办。我有一个暗示,作为QCoreApplication是一个QObject,我可以调用它的deleteLater()方法,但我看不到我应该从哪里调用它。
是否有专家了解QCoreApplication和QThread的来龙去脉,并提出解决方案?还是我设计的方式有根本的缺陷?
这似乎对我有用…
首先,我在全局命名空间中添加了一个静态的"cleanup"函数:
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仍在清理自己的过程中,可能会有冲突。我认为这在我使用它的上下文中不太可能发生
- 访问者访问变体并返回不同类型时出错
- 在Linux for Windows上编译C++代码时出错
- 读取文件的最后一行并输入到链接列表时出错
- 重载操作程序时出错>>用于类中的字符串 memebr
- 调用专用模板时出错"no matching function for call to [...]"
- C++-试图将函数指针推回到另一个CPP文件中的矢量时出错
- LINK 尝试使用 OpenSSL evp aes 256 c++ 时出错
- 在Google Kick start中提交时出错
- 在c++中访问int到类对象的映射时出错
- C++quit()函数中可能存在作用域问题
- 分段错误当我试图运行程序时出错
- 使用dynamic_cast和构造函数时出错
- CHECK(调用)函数在Google Colab中出错
- 用pybind11包装C++抽象类时出错
- 为x86而非x64编译时出错
- 从R调用C++函数并对其进行集成时出错
- 这个函数哪里出错了
- C++ 创建包含链表和字符串的对象的链接列表时出错
- 为重写std::exception的库生成swig接口时出错
- 在QThread中的QCoreApplication上调用quit()时出错