问题情况
程序运行时的样子:
程序退出时的样子:
其跑到了后台进程里面:
程序退出了,但在任务管理器里查看,其从进程里面转移到后台进程了。
这种问题,怎么办,代码里,应该释放的也都释放了,为什么会出这个问题呢?
在后台的进程,还有时占些cpu,说明的确在运行,的确在做事情。
这首先怀疑到了是否线程的问题呢?
问题分析
1、线程析构了吗?
窗体的析构函数:
MainWindow::~MainWindow()
{
if(nullptr != m_pCmdThread)
{
m_pCmdThread->deleteLater();
delete m_pCmdThread;
}
线程的析构函数:
CmdThread::~CmdThread()
{
quit();
wait();
}
看着都正常,问题出在那呢?
可能是quit(),wait()
quit()
告诉线程的事件循环以return 0(成功)退出。 相当于调用QThread :: exit(0)。如果线程没有事件循环,这个函数什么也不做。
wait()
阻塞线程,直到满足以下任一条件:
与此QThread对象关联的线程已经完成执行(即从run()返回)。 如果线程完成,该函数将返回true。 如果线程尚未启动,它也返回true。
时间毫秒已经过去了。 如果时间是ULONG_MAX(默认值),那么等待永远不会超时(线程必须从run()返回)。 如果等待超时,此函数将返回false。
这里非常关键的解释是quit(),quit(),不是线程退出,而是线程的事件循环退出,如果是继承的QThread,那么,自己实现了run方法,里面也没调用exec()方法,则这一个线程没有事件循环,这一点必须清楚。
没有事件循环,那么run方法的死循环就退不出来,我的代码是这样写的:
void CmdThread::run()
{
qDebug("enter function CmdThread::run");
m_pCmdService = new CmdService();
QString strMessage;
while(true)
{
switch (this->m_currentState)
{
case INIT_PORT:
case OPEN_PORT:
if(!m_pCmdService->openPort(*this->m_pBoardInfoDomain))
{
strMessage = QString::asprintf("串口%s连接失败", this->m_pBoardInfoDomain->serialName.c_str());
emit sendMessage(1, strMessage);
return;
}
显然,是没有事件循环的,那这样,线程还是在一 直运行的,那么这种情况如何解决,方法很多,目的就是退出这一个while循环就可以了,其实也可以这样说,没有一个合适的方法退出正在运行的线程,如果强制退出,那样是危险的。
为了正式一些,就用官方推荐的方法如下:
void CmdThread::run()
{
qDebug("enter function CmdThread::run");
m_pCmdService = new CmdService();
QString strMessage;
while(true)
{
if(QThread::currentThread()->isInterruptionRequested())
{
break;
}
switch (this->m_currentState)
{
case INIT_PORT:
case OPEN_PORT:
if(!m_pCmdService->openPort(*this->m_pBoardInfoDomain))
{
strMessage = QString::asprintf("串口%s连接失败", this->m_pBoardInfoDomain->serialName.c_str());
emit sendMessage(1, strMessage);
return;
}
触发的事件如下:
CmdThread::~CmdThread()
{
requestInterruption();
quit();
wait();
}
就是在析构的时候停止线程。
这样,再测试发现,关闭窗体后,后台进程不会再存在了,这样就解决了这一个问题,这一个问题的关键点在于:
1、对quit,exit函数的理解,其是退出线程事件,不会退出线程的run方法
2、线程的退出,只能我自己写代码实现,没有其它的办法,在析构函数里退出就是一个很好的方法。
如果一个应该停止的任务在此线程上运行,则返回true。 requestInterruption()可以请求中断。
这个功能可以用来使长时间运行的任务干净地中断。 建议在耗时长的运行函数中定期执行此操作。 注意不要太频繁地调用,以保持较低的开销。
最后,以一张图来说明情况: