2017-02-15 46 views
0

那么,我已经创建了一个程序,它从串行输入中获取输入信号。我可以通过UART成功接收来自设备的数据。我想在达到某些条件(例如接收超过5个字节等)后终止线程。我认为问题是如何正确终止Qt中的线程,但我找不到方法。调用子函数中的exec()后,该程序似乎陷入死锁。任何人都可以解决这个问题吗?非常感谢你!无法退出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 

这是the.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 

显然,该计划陷在一个循环。我尝试了一些解决方案,但都没有工作。

+1

此代码的简要扫描发现:'delete RunThread;返回a.exec();'不知道其他的事情,但这个显然是错误的。主线程在'a.exec()'中旋转,另一个线程在主线程进入其事件循环时应该处于活动状态? – AlexanderVX

回答

0

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

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

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

下面的例子显示了一个非常小的实现。总体来说,除了展示简洁性作为与问题中代码长度的对比之外,它并不是非常有用 - 尽管功能有限。据推测,你有一个更复杂的通信协议,你想处理。您不妨考虑使用QDataStream read transactions使阅读器代码更具表现力,并且using a state machine to represent the protocol

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

// See https://stackoverflow.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_port{this}; 
    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) : 
     QObject{parent} 
    { 
     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 ctl{ports.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"