无法在 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 循环的主要内容,如果未能解决你的问题,请参考以下文章