我有目前的情况:
Worker是MainWindow类中的一个字段,Watchdog是Worker类中的一个字段。
执行如下:
Worker
class Worker : public QThread
{
Q_OBJECT
public:
explicit Worker();
void run();
private:
Watchdog *watchdog;
bool running = false;
signals:
void tick();
public slots:
void ownerDied();
};
Worker
的构造函数在堆上构造Watchdog
class Watchdog : public QThread
{
Q_OBJECT
public:
Watchdog();
void run();
public slots:
void tick();
signals:
void ownerIsDead();
};
构造函数在QObject::connect()
和Watchdog
信号和广告位之间执行Worker
connect(this, SIGNAL(tick()), watchdog, SLOT(tick()));
connect(watchdog, SIGNAL(ownerIsDead()), this, SLOT(ownerDied()));
Worker
的主循环从Worker::run()
方法开始。
Worker
启动Watchdog
。 Watchdog
循环已启动。
如果Worker
在tick()
来电后的5秒内没有start()
,则Watchdog
会发出ownerIsDead()
信号
ownerDied()
信号,终止主Worker
循环Worker
勾选了Watchdog
,他会再睡5秒问题是,tick()
永远不会到达Watchdog
,ownerIsDead()
信号也不会到达工作人员,因为它没有打勾。为什么呢?
这是原始代码,类名有点不同。
watchdog.h
#ifndef WATCHDOG_H
#define WATCHDOG_H
#define THRESHOLD 1000
#include <QThread>
#include <QObject>
class Watchdog : public QThread
{
Q_OBJECT
public:
Watchdog();
void run();
public slots:
void tick();
void kill();
private:
bool running = false;
bool ticked = false;
signals:
void error();
};
#endif // WATCHDOG_H
watchdog.cpp
#include "watchdog.h"
#include <QDebug>
Watchdog::Watchdog()
{
}
void Watchdog::run()
{
running = true;
qDebug() << "Starting watchdog";
while (running) {
QThread::msleep(THRESHOLD);
qDebug() << "Watchdog tick ... ";
if (!ticked) {
qDebug() << "read() or write() is read";
emit error();
}
}
}
void Watchdog::tick()
{
qDebug() << "Watchdog ticking";
ticked = true;
}
void Watchdog::kill()
{
qDebug() << "Killing watchdog...";
running = false;
}
diskerror.h (AKA'工人')
#ifndef DISKERROR_H
#define DISKERROR_H
#include <QThread>
#include <watchdog.h>
extern "C" {
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <malloc.h>
}
class DiskError : public QThread
{
Q_OBJECT
public:
explicit DiskError();
void run();
private:
int mismatch(char *a, char *b);
Watchdog *watchdog;
bool running = false;
signals:
void tick();
void killWatchdog();
public slots:
void ownerIsDead();
};
#endif // DISKERROR_H
diskerror.cpp
include "diskerror.h"
#include "watchdog.h"
#include <QDebug>
#define BLKSZ 4096
DiskError::DiskError()
{
watchdog = new Watchdog();
connect(this, SIGNAL(killWatchdog()), watchdog, SLOT(kill()));
connect(this, SIGNAL(tick()), watchdog, SLOT(tick()));
connect(watchdog, SIGNAL(error()), this, SLOT(ownerIsDead()));
}
void DiskError::run()
{
int fd = open("/dev/sdc", O_RDWR | O_SYNC);
if (fd < 0) {
qDebug() << strerror(errno);
}
size_t size;
if (ioctl(fd, BLKGETSIZE64, &size) < 0) {
qDebug() << "IOCTL Error";
return;
}
size_t step = (size / 2500);
size_t done = 0;
int i = 0;
char testing[BLKSZ];
char pattern[BLKSZ];
for (int i = 0; i < BLKSZ; i++) {
pattern[i] = 0xCF;
}
int re, bb, wr;
off_t curr = 0;
watchdog->start();
running = true;
while (running) {
lseek(fd, curr, SEEK_SET);
wr = write(fd, pattern, BLKSZ); /* Write pattern to disk */
lseek(fd, curr, SEEK_SET);
re = read(fd, testing, BLKSZ); /* Read pattern back from disk */
bb = mismatch(pattern, testing);
curr += BLKSZ;
done += BLKSZ;
emit tick();
if ( (re == 0) || (wr < 0) ) {
qDebug() << "Flushing buffers...";
sync();
break;
}
if (done >= step) {
if (bb) {
qDebug() << "[" << i << "] Error occured";
} else {
qDebug() << "[" << i << "] OK";
}
done = 0;
i++;
}
}
emit killWatchdog();
sync();
if (close(fd) < 0) {
qDebug() << "Error closing device";
}
}
int DiskError::mismatch(char *a, char *b)
{
for (int i = 0; i < BLKSZ; i++) {
if ( (*(a+i)) != (*(b+i)) ) return 1;
}
return 0;
}
void DiskError::ownerIsDead()
{
qDebug() << "read() call blocked for more than 5 seconds, device inoperable";
}
我从未在worker类中看到调试文本,也没有在worker中看到tick文本。
答案 0 :(得分:2)
可能发生的事情是接收器对象“属于”不同于执行发射的线程。
这种跨线程信号/插槽连接,即所谓的Qt::QueuedConnection
连接,需要在接收器对象的线程中运行事件循环。
如果接收者对象是由其中一个附加线程创建的,那么该线程需要运行其事件循环,请参阅QThread::exec()
不确定你实际上是否需要将看门狗作为一个单独的线程,它似乎只是定期检查一个条件,主线程上的QTimer
也可以很容易地做。
答案 1 :(得分:0)
正如之前的评论和其他人的回答中所述,我认为核心问题是你的“线程”中没有事件循环,它负责管理(检查,详细说明和执行操作)信号/插槽。无限循环也是最糟糕的事情,因为它阻止你的线程/应用程序“调用”事件循环,因此信号/插槽系统变得无用。 您可以在此处找到更清晰,更广泛的表达方式:https://wiki.qt.io/Threads_Events_QObjects
允许你的while循环使用signal / slot的一个技巧是在每次迭代时调用函数 QCoreApplication :: processEvents(QEventLoop :: ProcessEventsFlags flags)。来自Qt docs:
根据以下内容处理调用线程的所有挂起事件 指定的标志,直到没有更多的事件要处理。
您可以在程序繁忙时偶尔调用此功能 执行长时间操作(例如复制文件)。
如果您正在运行调用此函数的本地循环 在没有事件循环的情况下,DeferredDelete事件将持续进行 不被处理。这可能会影响窗口小部件的行为,例如 QToolTip,依赖DeferredDelete事件正常运行。一个 另一种方法是从该本地调用sendPostedEvents() 循环。
调用此函数仅处理调用线程的事件。
最后一条评论:你实现线程的方式没有错,但也不是Qt建议的方式(但也许你已经选择知道我在说什么)。实际上,QThread对象不是线程,而是管理线程的对象。有关此问题的更多信息,请参阅Qt文档的这个链接:http://doc.qt.io/qt-4.8/qthread.html#details