无法在 Qt 中退出 exec 循环

Posted

技术标签:

【中文标题】无法在 Qt 中退出 exec 循环【英文标题】:Can't exit exec loop in Qt 【发布时间】:2017-02-15 05:46:11 【问题描述】:

好吧,我创建了一个程序,可以从串行输入中获取输入信号。我可以通过UART成功接收设备传输的数据。我想在达到某些条件后终止线程(例如接收超过5个字节等)我认为问题是如何正确终止Qt中的线程,但我找不到方法。在子函数中调用 exec() 后,程序似乎陷入了死锁。任何人都可以帮助解决这个问题吗?非常感谢你!

这是我的头文件:

#ifndef SERIALTHREAD
#define SERIALTHREAD

#include <QtSerialPort/QSerialPort>
#include <QDebug>

#include <QString>
#include <QThread>
#include <QtCore>

#include <iostream>
#include <fstream>


class SerialControlThread : public QThread

   Q_OBJECT
  public:

  explicit SerialControlThread(QString ComPort,QObject *parent = 0);
  ~SerialControlThread(); // Destructor

    bool openSerialPort();
    void closeSerialPort();
    void run();
    bool TelltoExit();
    void StarttoRun();


   private:

     int DataCount;

     QString ComPortNumber;
     QSerialPort *serial;

     int* VoltageStorage; // Total 3 channels, each channel takes 10 data

     unsigned int Channel_A[10]; // Channel_A is for Phase Tx s

     int DataCountIndexA; // This is how many data has been sent to the buffer;

     int SentDataCount;
     unsigned char StoreDataBuffer[2];

     unsigned char TotalDataCounter;

     std::ofstream write;

  signals:
      void BufferisFull(int*);
      void TimeToQuit();



public slots:


private slots:
    void readData();
    void handleError(QSerialPort::SerialPortError error);

;

#endif // SERIALTHREAD

这是.cpp

#include "serialcontrol.h"
#include <iostream>

SerialControlThread::SerialControlThread(QString ComPort,QObject *parent) :
    QThread(parent),ComPortNumber(ComPort)


    DataCountIndexA=0;

    DataCount=0;

    serial = new QSerialPort(this);

    connect(this,SIGNAL(TimeToQuit()),this,SLOT(quit()));\
    connect(serial, SIGNAL(readyRead()), this, SLOT(readData()));
    connect(serial, SIGNAL(error(QSerialPort::SerialPortError)), this,
            SLOT(handleError(QSerialPort::SerialPortError)));

    for (int i=0;i<10;i++)
      Channel_A[i]=0;




SerialControlThread::~SerialControlThread()


    this->closeSerialPort();
    delete serial;


bool SerialControlThread::openSerialPort()


  //  std::cout << "Hey I am in serial function" << std::endl;

    serial->setPortName(ComPortNumber) ;
    serial->setBaudRate(QSerialPort::Baud9600); //This can be set through menu in the future
    serial->setDataBits(QSerialPort::Data8); // A packets contains 8 bits ( 3 for signature bits)
    serial->setParity(QSerialPort::NoParity);
    serial->setStopBits(QSerialPort::OneStop);
    serial->setFlowControl(QSerialPort::NoFlowControl);

    if (!(serial->open(QIODevice::ReadWrite))) 

    return false;  // return false when the device can't be opened
     else
    
     return true; // return true when the device is avalaible



void SerialControlThread::closeSerialPort()

    if (serial->isOpen())
        serial->close();


void SerialControlThread::handleError(QSerialPort::SerialPortError error)





void SerialControlThread::readData()

    QByteArray data=serial->read(100);
    const char *TempChar=data.data();

    std::cout << TempChar << std::endl;

    DataCount++;

   if(DataCount>=4)
    
       std::cout << "I am bigger than 4" << std::endl;
       this->quit();
     
   



void SerialControlThread::run()







bool SerialControlThread::TelltoExit()




void SerialControlThread::StarttoRun()


        // Sending the msp430 S to activate the following sequence
            const char *temp="S";
           serial->write(temp);
           serial->waitForBytesWritten(30000);
           this->exec();


这是main.cpp

#include <QCoreApplication>
#include <QtSerialPort/QSerialPortInfo>
#include <QList>
#include <iostream>
#include <QString>
#include <QDebug>
#include <QSerialPort>

#include "serialcontrol.h"

using namespace std;


int main(int argc, char *argv[])

    QCoreApplication a(argc, argv);



   int AvailablePorts=QSerialPortInfo::availablePorts().count();
   QList<QSerialPortInfo> SerialObject=QSerialPortInfo::availablePorts();


   cout <<  "There are total: " << SerialObject.count() <<  " available ports " << endl << endl;


   QString description;



   for (int i=0;i<AvailablePorts;i++)
   
       cout << "The " << i+1 << " com port is :";
       qDebug() <<  SerialObject[i].portName();
       qDebug() << "Description : " <<  SerialObject[i].description();
       qDebug() << "Manufacturer: " <<  SerialObject[i].manufacturer();

       cout << endl;
   



   SerialControlThread *RunThread=new SerialControlThread(SerialObject[0].portName(),&a);


   cout << RunThread->openSerialPort() << endl;
   RunThread->StarttoRun();




   cout << "I am out of here" << endl;


    delete RunThread;






    return a.exec();

我希望在缓冲区接收到超过 4 个数据时关闭线程(返回主函数),但它没有。

这是我的输出

There are total: 1 available ports

The 1 com port is :"COM8"
Description :  "MSP430 Application UART"
Manufacturer:  "Texas Instruments"

1
0
1
2
3
I am bigger than 4
4
I am bigger than 4
5
I am bigger than 4
6
I am bigger than 4
7
I am bigger than 4
8
I am bigger than 4
9
I am bigger than 4

显然,程序陷入了循环。我尝试了一些解决方案,但这些都不起作用。

【问题讨论】:

简要扫描这段代码发现:` delete RunThread; return a.exec();` 不确定其他事情,但这显然是错误的。主线程在a.exec() 中旋转,而当主线程进入其事件循环时,另一个线程应该处于活动状态? 【参考方案1】:

StartToRun 在错误的线程中调用 QThread::exec:您在主线程中调用它,但它应该在线程本身中调用 - 从 run() 中调用。

唉,SerialControlThread 不一定是线程。使其成为线程会强制它在专用线程中使用 - 这应该是留给用户的选择。也许该线程将在其他串行控制器之间共享,或者它可能在主线程中做得很好。因此,它应该是一个处理串行数据的对象,它具有线程安全接口,以便您可以根据需要将其移动到另一个线程 - 但在主线程中仍然可以正常工作,因此必须异步处理数据而无需阻塞。

考虑是否需要如此严格地控制工作线程的运行状态:空闲线程不消耗资源——它的事件循环被阻塞等待新事件,如果内存压力,它的堆栈最终会被分页。如果打算为每个操作“唤醒”线程,则无需明确说明:线程中的事件循环的行为方式是默认和设计的:它在有新事件时唤醒,例如传入数据,否则它会睡觉。那时不应该停止线程。

下面的示例显示了一个非常简单的实现。总的来说,除了证明简洁性与问题中的代码长度形成对比之外,它并不是很有用 - 尽管功能相同但有限。大概您有一个更复杂的通信协议要处理。您不妨考虑使用QDataStream read transactions 使阅读器代码更具表现力,以及using a state machine to represent the protocol。

// https://github.com/KubaO/***n/tree/master/questions/serial-galore-42241570
#include <QtWidgets>
#include <QtSerialPort>

// See https://***.com/q/40382820/1329652
template <typename Fun> void safe(QObject * obj, Fun && fun) 
   Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
   if (Q_LIKELY(obj->thread() == QThread::currentThread() || !obj->thread()))
      return fun();
   struct Event : public QEvent 
      using F = typename std::decay<Fun>::type;
      F fun;
      Event(F && fun) : QEvent(QEvent::None), fun(std::move(fun)) 
      Event(const F & fun) : QEvent(QEvent::None), fun(fun) 
      ~Event()  fun(); 
   ;
   QCoreApplication::postEvent(
            obj->thread() ? obj : qApp, new Event(std::forward<Fun>(fun)));


class SerialController : public QObject 
   Q_OBJECT
   QSerialPort m_portthis;
   QByteArray m_rxData;

   void onError(QSerialPort::SerialPortError error) 
      Q_UNUSED(error);
   
   void onData(const QByteArray & data) 
      m_rxData.append(data);
      qDebug() << "Got" << m_rxData.toHex() << "(" << m_rxData.size() << ") - done.";
      emit hasReply(m_rxData);
   
   void onData() 
      if (m_port.bytesAvailable() >= 4)
         onData(m_port.readAll());
   
public:
   explicit SerialController(const QString & port, QObject * parent = nullptr) :
      QObjectparent
   
      m_port.setPortName(port);
      connect(&m_port, static_cast<void(QSerialPort::*)(QSerialPort::SerialPortError)>(&QSerialPort::error),
              this, &SerialController::onError);
   
   ~SerialController()  qDebug() << __FUNCTION__; 
   bool open() 
      m_port.setBaudRate(QSerialPort::Baud9600);
      m_port.setDataBits(QSerialPort::Data8);
      m_port.setParity(QSerialPort::NoParity);
      m_port.setStopBits(QSerialPort::OneStop);
      m_port.setFlowControl(QSerialPort::NoFlowControl);
      return m_port.open(QIODevice::ReadWrite);
   
   /// This method is thread-safe.
   void start() 
      safe(this, [=]
         m_port.write("S");
         qDebug() << "Sent data";
      );
   
   Q_SIGNAL void hasReply(const QByteArray &);
   void injectData(const QByteArray & data) 
      onData(data);
   
;

QDebug operator<<(QDebug dbg, const QSerialPortInfo & info) 
   dbg << info.portName();
   if (!info.description().isEmpty())
       dbg << " Description: " <<  info.description();
   if (!info.manufacturer().isEmpty())
       dbg << " Manufacturer: " <<  info.manufacturer();
   return dbg;


// A thread that starts on construction, and is always safe to destruct.
class RunningThread : public QThread 
   Q_OBJECT
   using QThread::run; // final
public:
   RunningThread(QObject * parent = nullptr) : QThread(parent)  start(); 
   ~RunningThread()  qDebug() << __FUNCTION__; quit(); wait(); 
;

int main(int argc, char *argv[])

   QCoreApplication app(argc, argv);
   auto const ports = QSerialPortInfo::availablePorts();
   if (ports.isEmpty())
      qFatal("No serial ports");

   int n;
   qDebug() << "Available ports:";
   for (auto & port : ports)
      qDebug() << "port[" << n++ << "]: " << port;

   SerialController ctlports.at(5).portName();
   if (!ctl.open())
      qFatal("Open Failed");

   // Optional: the controller will work fine in the main thread.
   if (true) ctl.moveToThread(new RunningThread&ctl); // Owns its thread

   // Let's pretend we got a reply;
   QTimer::singleShot(1000, &ctl, [&ctl]
      ctl.injectData("ABCD");
   );
   QObject::connect(&ctl, &SerialController::hasReply, ctl.thread(), &QThread::quit);
   QObject::connect(&ctl, &SerialController::hasReply, [&]
      qDebug() << "The controller is done, quitting.";
      app.quit();
   );
   ctl.start();
   return app.exec();

#include "main.moc"

【讨论】:

以上是关于无法在 Qt 中退出 exec 循环的主要内容,如果未能解决你的问题,请参考以下文章

Qt exec()的返回值是多少啊

QT 线程从不运行

Qt ------ 主事件循环与 QEventLoop

Qt 调试崩溃/退出 python 并最终无法正确调试(例如,不在断点处停止)

Js中数组的forEach()方法return无法退出循环

如何让Qt 的程序等待一段时间