【问题标题】:Signal and slot mechanism not working across non-UI threads信号和槽机制不适用于非 UI 线程
【发布时间】:2016-10-16 14:28:37
【问题描述】:

我有现在的情况:

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();
    
    };
    
    • 构造函数在WatchdogWorker 信号和槽之间执行QObject::connect()

      connect(this, SIGNAL(tick()), watchdog, SLOT(tick()));
      connect(watchdog, SIGNAL(ownerIsDead()), this, SLOT(ownerDied()));
      
    • Worker 的主循环以 Worker::run() 方法开始。

    • Worker 启动 WatchdogWatchdog 循环启动。

    • 如果Workerstart() 调用后5 秒内没有tick(),则Watchdog 发出ownerIsDead() 信号

    • Worker 处理 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(又名“工人”)

#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 中看到刻度文本。

【问题讨论】:

  • 您需要在每个要处理插槽的线程中运行一个事件循环。在您的 Watchdog 类中,您不需要使用无限循环覆盖 run 方法。相反,您需要在此线程中创建一个带有计时器的对象。

标签: c++ multithreading qt


【解决方案1】:

可能发生的情况是接收器对象“属于”与执行发射的线程不同的线程。

这种跨线程的信号/槽连接,即所谓的Qt::QueuedConnection 连接,需要在接收器对象的线程中运行事件循环。

如果接收器对象已由其他线程之一创建,则该线程需要运行其事件循环,请参阅QThread::exec()

不确定你是否真的需要看门狗作为一个单独的线程,它似乎只定期检查一个条件,主线程上的 QTimer 也可以轻松完成。

【讨论】:

    【解决方案2】:

    正如之前的 cmets 和其他人的回答中所述,我认为核心问题是您的“线程”中没有事件循环,它负责管理(检查、详细说明和执行操作)信号/插槽.无限循环也是最糟糕的事情,因为它会阻止您的线程/应用程序“调用”事件循环,从而使信号/插槽系统变得无用。 你可以在这里找到更清晰和广泛的解释:https://wiki.qt.io/Threads_Events_QObjects

    让 while 循环使用信号/槽的一个技巧是在每次迭代时调用函数 QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)。来自 Qt 文档:

    根据 指定标志,直到没有更多要处理的事件。

    您可以在程序繁忙时偶尔调用此函数 执行长时间操作(例如复制文件)。

    如果您正在运行调用此函数的本地循环 连续,没有事件循环,DeferredDelete 事件将 不被处理。这可能会影响小部件的行为,例如 QToolTip,依赖 DeferredDelete 事件才能正常运行。一个 替代方法是从该本地调用 sendPostedEvents() 循环。

    调用这个函数只为调用线程处理事件。

    最后一条评论:您实现线程的方式没有错,但也不是 Qt 建议的方式(但也许您选择了知道我在说什么的方式)。实际上,QThread 对象不是线程,而是管理线程的对象。在此 Qt 文档链接中了解更多信息:http://doc.qt.io/qt-4.8/qthread.html#details

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      • 2014-11-12
      • 2020-07-09
      • 1970-01-01
      相关资源
      最近更新 更多