一、QProcess的阻塞模式
QProcess的应用场景非常广泛。可以使用它在qt程序中执行其他进程,并与之进行通信。
当使用它执行一些终端命令和操作时,命令和操作往往是需要一定的时间的,这时QProcess本身提供了方法如:
waitForStarted() //启动阻塞,等待程序启动完毕,期间整个程序所有进程阻塞
waitForFinished() //结束阻塞,等待程序结束完毕,期间整个程序所有进程阻塞
这些方法,在主线程中使用时,都是会阻塞主线程的。
其中,以waitForFinished()为例,看函数说明就可以知道
它有一个参数,而且有默认值,它默认qprocess执行的功能最长可以运行30秒,仍未执行完,则结束它。当你把参数设置为-1时,还可以一直阻塞至程序执行完成。
二、阻塞模式的问题。
当你的程序是一个图形界面程序时,这也是qt常见的应用场景,在主线程中使用waitForFinish,则在阻塞期间,程序是无法响应界面的操作的,对外会表现为界面卡顿,而一些场景下,你希望在waitForFinish时,显示一个进度框,也是会卡的。
三、一种非阻塞式的解决方法
那么,如何实现qprocess既能有时间执行相关操作,同时,又不会阻塞呢?方法之一就是使用:
QApplication::processEvents。
它将处理所有事件队列中的事件并返回给调用者。
该函数的作用是让程序有机会去处理那些还没有处理的事件,然后再把使用权返回给调用者。
具体的解释如下:
举例:我们可以写一个非阻塞式的函数:
void ******::delayMSecs(int msec)
{
QTime Time_set = QTime::currentTime().addMSecs(msec);
while( QTime::currentTime() < Time_set )
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
其中processEvents参数的意义,参看上面的函数解释。
这样,可以如下使用:
m_qprocess->start(***********);
delayMSecs(1000);
会留出需要的时间,去执行start里的命令。
四,使用QApplication::processEvents时需要注意的一个问题。
这个问题就是:它可能会引起递归,导致栈溢出崩溃
举例:
bugThread.h
#include <QThread>
class BugThread : public QThread
{
Q_OBJECT
public:
BugThread(QObject* parent) : QThread(parent) {}
signals:
void sigBugsignal();
public:
void run()
{
while(true)
{
emit sigBugsignal();
}
}
};
demo.h & demo.cpp
class Demo : public QMainWindow
{
Q_OBJECT
public:
Demo(QWidget *parent = 0, Qt::WFlags flags = 0);
~Demo();
public slots:
void onBugSlot();
private:
Ui::BugsClass ui;
};
Demo::Demo(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
BugThread* bt = new BugThread(this);
connect(bt, SIGNAL(sigBugsignal()), this, SLOT(onBugSlot()));
bt->start();
}
void Demo::onBugSlot()
{
Sleep(1);
QApplication::processEvents();
}
如上面的代码所示:
当主线程在某个槽函数里正在执行processEvents时, 刚好有一个能响应此槽函数的信号发送过来了(肯定是其他线程发的信号), 这时就可能会发生可怕的递归,
导致栈溢出崩溃。 原因是processEvents,进入到无尽的递归中。
使用时一定注意,一定注意。
五,非阻塞方式的一些错误用法。
错误方式1:sleep
界面的线程是主线程,在主线程中使用休眠函数是一种错误,这会直接导致界面无法刷新,用户与程序无法交互,最终导致程序崩溃。linux提供的”sleep”或”usleep”函数会将你当前的线程/进程变为“睡眠”状态。 这个“睡眠”是深度意义的睡眠, 睡眠期间内核不会分配给程序时间片, 所以程序什么都不做, 更不用提界面的刷新了。
错误方式2:死循环
QTime time;
time.start();
while(time.elapsed() < 5000);
如上所示,当在死循环的时候,我们的界面是无法刷新,用户是不会响应用户的任何交互的。也就是让用户感觉程序已经是假死状态了。 从代码中我们可以发现在while循环中不停的调用elapsed()函数, 等于在这段时间内CPU完全没有机会做别的什么事情。 特别是在Linux这样非抢占式的操作系统中, 这样的死循环造成的影响是致命的, CPU被完全占用, 内核都没有机会调度进程, 别的程序拿不到时间片执行, 系统基本上就是瘫痪状态了。