我正在努力完成看起来像一个非常简单的任务…但它是一个噩梦,而不是。
我为Linux笔记本开发了一个应用程序。 在应用程序内部,我想有一个独立的线程,不断ping另一台PC(例如,每5秒一次,永远…以及,只要笔记本电脑打开)。
当然,当应用程序正在ping的PC没有连接,应用程序必须工作顺利,而不必等待ping操作返回…我怎么能做到这一点?
起初,我用QProcess:execute("ping"...)
,这工作正常。 问题是,如果其他PC没有回复,我的整个应用程序和它的graphics用户界面在每次ping操作时都会冻结一秒左右。 更改“ping”选项(例如,设置“-i0.2”),以减lessping操作的等待时间,没有帮助:当其他PC未连接时,我的应用程序变得非常慢。 如果我删除ping,当然一切正常。
所以,我决定在QThread
插入“ping”操作,但是当我尝试按照http://doc.qt.io/qt-4.8/qthread.html中的第二个示例时,似乎没有任何工作:应用程序甚至没有开始。
代码如下:
//Pinger.h class Pinger : public QThread { Q_OBJECT void run(); public: void setParam(const QString &urlToPing); // it sets the url to ping signals: /// \brief Signal emitted when pinging of specified url fails void pingFailed(int ok); private: QString pingurl; }; //Pinger.cpp void Pinger::run() { int exitCode; QProcess pingProc; while(true) { exitCode=pingProc.execute("ping",QStringList() << "-c 1" << "-i0.2" << pingurl); emit pingFailed(exitCode); sleep(5); } } // MainWindow::MainWindow pinga= new Pinger(); // defined in mainwindow.h as Pinger* Pinga pinga->setParam(ip_address); connect(pinga,SIGNAL(pingFailed(int)),this,SLOT(connectionLost(int))); connect(pinga,SIGNAL(finished()),pinga,SLOT(deleteLater())); pinga->start();
有没有人试过类似的东西? 我对Qt相当陌生,但是这个操作看起来很微不足道,我觉得不可能实现它。 所以我希望我只是失去了一些明显的东西。
如果使用QThread
,最好避免sleep(5);
和while循环while(true)
,因为线程不能被正常关闭而不会被杀死。 当前一个任务(过程执行)完成时,通过单次启动计时器再次调用该周期性任务,而不是循环和阻塞睡眠。 然而,在这种情况下,这个逻辑应该在其他成员槽( Pinger::doWork()
)中实现。 slot run()
应该保持其执行tread事件循环的默认实现。 工作可以通过连接QThread::started()
信号和Pinger::doWork()
:
connect(pinga, SIGNAL(started()), pinga, SLOT(doWork()));
QThread
删除需要小心。 一般来说,自己删除QThread
对象是deleteLater()
从其finished()
信号中调用deleteLater()
)。 最好停止线程并在MainWindow
析构函数中删除它:
MainWindow::~MainWindow { // stop the even loop pinga->quit(); // wait for finishing current thread task; it can work only // if the thread is not blocked by while(true) with sleep pinga->wait(); // delete if it is not a smart pointer delete pinga; }
也可以在没有QThread
的主GUI线程中使用带有非阻塞API的QProcess
。 在这种情况下,它应该由QProcess::start()
并且连接到信号QProcess::error()
和QProcess::finished()
的槽应该用于开始下一次迭代。 这些插槽也不应该阻塞主线程,所以一旦前一个ping完成,下一个ping应该使用QTimer
启动。
这是一个写你的Pinger类的“Qt方法”的例子。 请注意,不需要线程。 QProcess是异步使用的,并通过Qt信号报告其状态。 一旦你真的很喜欢Qt,你会意识到使用线程很少是这些类型的问题的正确或最自然的解决方案。
请注意,我正在使用支持C ++ 11支持的Qt 5来将Qt信号连接到C ++ 11 lambda表达式…您可以使用Qt4样式轻松编写它,但是它不会那么紧凑和可读。
class Pinger : public QObject { Q_OBJECT public: Pinger(QObject *parent = 0) : QObject(parent) { //Have to do this ugliness because QProcess::finished is overloaded auto finishedFunc = static_cast<void(QProcess::*)(int)>(&QProcess::finished); connect(&m_process, finishedFunc, [this](int exitCode) { if( exitCode == 0 ) { emit pingSuccess(); } else { emit pingFailed(exitCode); } }); } void run(const QString& hostToPing, int intervalInSeconds) { m_host = hostToPing; QTimer* timer = new QTimer(this); timer->start(intervalInSeconds * 1000); QObject::connect(timer, &QTimer::timeout, [this]() { if ( m_process.state() == QProcess::NotRunning ) { m_process.start(QString("ping -c 1 -W 1 %1").arg(m_host)); } else { qDebug() << "Cannot ping, previous ping operation still in progress!"; } }); } signals: void pingSuccess(); void pingFailed(int exitCode); private: QProcess m_process; QString m_host; };
使用它如下简单:
Pinger pinger; QObject::connect(&pinger, &Pinger::pingSuccess, []() { qDebug() << "Host is up!"; }); QObject::connect(&pinger, &Pinger::pingFailed, [](int exitCode) { qDebug() << "Host is unreachable! Ping exit code = " << exitCode; }); pinger.run("google.com", 3);