信号和插槽机制无法跨非UI线程

时间:2016-10-16 14:28:38

标签: c++ multithreading qt

我有目前的情况:

enter image description here

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启动WatchdogWatchdog循环已启动。

    • 如果Workertick()来电后的5秒内没有start(),则Watchdog会发出ownerIsDead()信号

    • 工作人员处理ownerDied()信号,终止主Worker循环
    • 如果Worker勾选了Watchdog,他会再睡5秒
    • 整个过程重复

问题是,tick()永远不会到达WatchdogownerIsDead()信号也不会到达工作人员,因为它没有打勾。为什么呢?

这是原始代码,类名有点不同。

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文本。

2 个答案:

答案 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