我正在努力完成看起来非常简单的任务......但它确实是一场噩梦。
我为Linux笔记本电脑开发了一个应用程序。在应用程序内部,我想有一个独立的线程,持续ping另一台PC(例如每5秒一次,永远......好吧,只要笔记本电脑打开)。
当然,当应用程序ping的PC没有连接时,应用程序必须工作顺利,无需等待ping操作返回...我怎样才能实现此目的?
起初我使用QTimer
和QProcess:execute("ping"...)
,效果很好。问题是,如果其他PC没有回复,我的整个应用程序及其GUI在每次ping操作时都会冻结大约一秒钟。改变" ping"选项(设置" -i0.2",例如),为了减少ping操作的等待时间,没有帮助:当另一台PC未连接时,我的应用程序变得非常慢。如果我删除ping,当然一切都很顺利。
所以,我决定插入" ping"在QThread
中操作,但当我尝试按照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很新,但是这个操作看起来很微不足道,以至于我觉得没有明确的方法可以实现它。所以我希望我错过了一些明显的东西。
答案 0 :(得分:2)
如果使用QThread
,最好避免使用sleep(5);
和循环while(true)
,因为线程无法正常关闭而不会将其删除。而不是循环和阻塞睡眠,最好通过在前一个任务(流程执行)完成时启动的单次定时器再次调用该周期性任务。但是,在这种情况下,逻辑应该在其他成员槽(Pinger::doWork()
)中实现。插槽run()
应保留其执行踏板事件循环的默认实现。可以通过将QThread::started()
信号与Pinger::doWork()
:
connect(pinga, SIGNAL(started()), pinga, SLOT(doWork()));
删除QThread
时需要小心。通常,单独删除QThread
对象(从deleteLater()
信号中调用finished()
)并不好。最好停止线程并在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;
}
也可以在没有QProcess
的主GUI线程中使用QThread
及其非阻塞API。在这种情况下,它应该由QProcess::start()
启动,并且应该使用连接到信号QProcess::error()
和QProcess::finished()
的插槽来开始下一次迭代。这些插槽也不应该阻塞主线程,所以下次ping应该在前一次ping完成后使用QTimer
启动。
答案 1 :(得分:1)
以下是编写Pinger课程的“Qt Way”示例。请注意,不需要线程。 QProcess以异步方式使用,并通过Qt信号报告其状态。一旦你真正理解了Qt,你就会意识到使用线程很少是解决这些类型问题的正确或最自然的解决方案。
请注意,我正在使用支持C ++ 11的Qt 5将Qt信号连接到C ++ 11 lambdas ...你可以很容易地用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);
答案 2 :(得分:0)
我遇到了同样的问题,并通过在自己的线程中运行ping
命令来解决此问题。通过使用信号,我正在与非阻塞GUI进行交互。
1)我声明了一个Ping类,该类运行Ping命令:
class Ping {
public:
static bool start(QString host) {
QStringList parameters;
#if defined(WIN32)
parameters << "-n" << "1";
#else
parameters << "-c 1";
#endif
parameters << host;
int exitCode = QProcess::execute("ping", parameters);
if (exitCode==0) {
return true;
} else {
return false;
}
}
};
2。)另外,我还有一个NetworkReceiver类,它通过一个插槽(startConnectionCheck
)创建网络线程,并通过一个信号(newConnectionStatus
)与GUI进行交互:
class NetworkReceiver : public QObject
{
Q_OBJECT
public:
explicit NetworkReceiver(QObject * parent = nullptr);
~NetworkReceiver();
void start() {
while(true) {
emit newConnectionStatus(Ping::start("google.at"));
QThread::sleep(5);
}
}
signals:
void newConnectionStatus(bool connected);
public slots:
void startConnectionCheck() {
QFuture<void> test = QtConcurrent::run(this, &NetworkReceiver::start);
}
};