QThread中当前的子线程出现了msleep,配合invokeMethod出现死锁问题

先看当前代码

点击查看代码
class SafetyWork:public QObject
{
    Q_OBJECT
public:
    explicit SafetyWork(MotionCardInterface*card,QObject *parent = nullptr);
public slots:
    void startMotion(int intervalMs=100);//开始检查
    void stopMotion();//停止检查
    void Checking();//具体的检查
signals:
    void SafetyError(int code,const QString&errorMessage);//安全检查出现问题
    void safetyStatusChanged(bool isSafe);//安全状态改变
private:
    MotionCardInterface*m_card=nullptr;//控制卡
    bool m_running=false;//是否在运行
    //std::atomic<bool> m_running{false};
};

class SafetyMonitor : public QObject
{
    Q_OBJECT
public:
    explicit SafetyMonitor(MotionCardInterface*card,QObject *parent = nullptr);
    ~SafetyMonitor();
    void start(int intervalMs=100);//开始监控检查
    void stop();//结束监控检查
signals:
    void SafetyError(int code,const QString&errorMessage);//安全检查出现问题
    void safetyStatusChanged(bool isSafe);//安全状态改变
private:
    QThread*m_thread;
    SafetyWork*m_safetyWork;
};
//启动检查
void SafetyWork::startMotion(int intervalMs)
{
    this->m_running=true;
    while(this->m_running)
    {
        this->Checking();
        QThread::msleep(intervalMs);
    }
}
//停止检查
void SafetyWork::stopMotion()
{
    this->m_running=false;
}
void SafetyWork::Checking()
{
    if(!this->m_card)return;
    bool estop = false;//急停释放
    bool doorClosed = false;//安全门是否正常关闭
    bool airPressure = false;//气压是否正常
    this->m_card->readInput(0,estop);
    this->m_card->readInput(1,doorClosed);
    this->m_card->readInput(2,airPressure);
    if(!estop)
    {
        emit SafetyError(0,"急停未释放");
        emit safetyStatusChanged(false);
        return;
    }
    if(!doorClosed)
    {
        emit SafetyError(1,"安全门未关闭");
        emit safetyStatusChanged(false);
        return;
    }
    if(!airPressure)
    {
        emit SafetyError(2,"气压出现问题");
        emit safetyStatusChanged(false);
        return;
    }
    emit safetyStatusChanged(true);
}

SafetyMonitor::SafetyMonitor(MotionCardInterface *card, QObject *parent)
{
    this->m_thread=new QThread(this);
    this->m_safetyWork=new SafetyWork(card);
    this->m_safetyWork->moveToThread(this->m_thread);
    connect(this->m_safetyWork,&SafetyWork::SafetyError,this,&SafetyMonitor::SafetyError);
    connect(this->m_safetyWork,&SafetyWork::safetyStatusChanged,this,&SafetyMonitor::safetyStatusChanged);
}
SafetyMonitor::~SafetyMonitor()
{
    this->stop();
}
//启动监控检查
void SafetyMonitor::start(int intervalMs)
{
    if(this->m_thread->isRunning())return;
    this->m_thread->start();
    //只需要单次调度即可,否则用connect也可
    QMetaObject::invokeMethod(this->m_safetyWork, "startMotion",
                              Qt::QueuedConnection,
                              Q_ARG(int, intervalMs));
    //connect(this->m_thread,&QThread::started,this->m_safetyWork,&SafetyWork::startMotion,Qt::QueuedConnection);
    
}
//结束监控检查
void SafetyMonitor::stop()
{
    if(this->m_thread->isRunning())
    {
         QMetaObject::invokeMethod(this->m_safetyWork,
             "stopMotion", Qt::BlockingQueuedConnection);
        this->m_thread->quit();
        this->m_thread->wait();
        if (m_safetyWork) {
            delete m_safetyWork;
            m_safetyWork = nullptr;
        }
    }
}
在这里面SafetyMonitor中的SafetyWork类被移入了子线程中,整个暂停逻辑是:调用SafetyMonitor::stop()、然后由于invokeMethod的存在,会把SafetyWork::stopMotion()放入子线程的事件循环,然后ui主线程被阻塞直至子线程处理完SafetyWork::stopMotion() 但是问题出现在这里,子线程当前在执行SafetyWork::startMotion()中的while循环,会向主线程发射信号并msleep一段时间 所以当子线程向主线程发送信号的时候,由于主线程在等待子线程处理SafetyWork::stopMotion()被阻塞了,主线程接受不到,然而子线程就会一直等待,这样两者就被互相阻塞 这就是死锁问题 **那怎么解决** 那就是不要把当前的函数进入子线程的事件循环,直接用原子变量atomic用来控制while循环,到时候stop直接把running改为false,while就会停了 因为原子变量可以保证读写时的安全,可以解决读写的竞争问题 这样:
点击查看代码
std::atomic<bool> m_running{false};
void SafetyWork::startMotion(int intervalMs)
{
    this->m_running=true;
    while(this->m_running.load())
    {
        this->Checking();
        QThread::msleep(intervalMs);
    }
}
当然我们对于多线程轮询最好不用hwhile中sleep进行时间间隔,最好用timer定时器进行定时查询

posted on 2026-07-30 16:48  dd_l  阅读(7)  评论(0)    收藏  举报