【发布时间】:2019-01-21 03:21:30
【问题描述】:
我正在开发一个涉及与一个或多个设备进行串行通信的 Qt 应用程序。有不同的程序可以同时执行,每个程序可以向设备发送一个或未知数量的命令,并可以接收数据作为响应。为了更清楚,这里是场景的图形说明:
点击一个按钮会触发相应过程的执行。 因此,当用户在短时间内单击两个或多个按钮时,可能会同时运行两个或多个不同的程序。实际上,它们之间唯一可以共享的是与单个设备的串行通信;否则它们大多是相互独立的。下面是两个伪代码示例,说明程序的外观:
程序 A:
begin
write command a1 on serial port
wait for one second
perform some computations
write command a2 on serial port
wait for one second
end
程序 B:
begin
while true:
write command b1 on serial port
read the response from serial port
perform some computations
if a condition holds return, otherwise continue
end
我的解决方案及其问题:
为了简化情况,我们只需要与一台设备进行通信。由于程序可以同时执行(一次只有一个程序可以通过串行端口与设备通信),我为每个程序创建了一个线程和一个工作人员类,并将工作人员移动到相应的线程。为了在访问串行端口时同步程序,我创建了一个互斥锁:
MainWindow.h
class MainWindow : public QMainWindow {
public:
//...
QSerialPort* serial_;
QMutex serial_mutex_;
private:
//...
ProcAWorker* proca_worker;
ProcBWorker* procb_worker;
ProcCWorker* procc_worker;
ProcDWorker* procd_worker;
QThread proca_thread;
QThread procb_thread;
QThread procc_thread;
QThread procd_thread;
}
MainWindow.cpp
void MainWindow::onConnectButtonClicked()
{
serial_ = new QSerialPort();
// configure serial port settings
serial_->open(QIODevice::ReadWrite);
}
void MainWindow::onButtonAClicked()
{
proca_worker = new ProcAWorker(0, this); // pass a pointer to this class to be able to access its methods and members
proca_worker->moveToThread(&proca_thread);
// setup worker-thread connections: started, quit, finished, etc.
proca_thread.start(); // triggers `proccess` slot in proca_worker
}
// same thing for other buttons and procedures
ProcAWorker.cpp
void ProcAWorker::ProcAWorker(QObject *parent, QMainWindow *wnd) :
QObject(parent), wnd_(wnd)
{
}
void ProcAWorker::process()
{
wnd_->serial_mutex_->lock();
wnd_->serial_->write('Command a1'); // Warning occurs in this line
bool write_ok = client_->serial_->waitForBytesWritten(SERIAL_WRITE_TIMEOUT);
wnd_->serial_mutex_->unlock();
QThread::sleep(1);
// perform some computations
wnd_->serial_mutex_->lock();
wnd_->serial_->write('Command a2');
bool write_ok = client_->serial_->waitForBytesWritten(SERIAL_WRITE_TIMEOUT);
wnd_->serial_mutex_->unlock();
if (write_ok) {
// signal successful to main window
emit success();
}
}
但是,当在串行端口(即wnd_->serial_->write('Command a1');)上执行写操作时,会显示以下警告:
QObject:无法为不同的父级创建子级 线。 (父级为 QSerialPort(0x18907d0),父级线程为 QThread(0x13cbc50),当前线程为QThread(0x17d8d08)
我的问题:
1) 我已经在 Stackoverflow 上查看了有关此警告的其他问题,但他们的回答只提到应该使用信号/插槽。我熟悉使用信号/插槽与工作线程进行通信。但是,我无法弄清楚如何使用信号/插槽来实现我的特定场景(同时运行具有串行端口等共享资源的程序),或者如何修改我当前的解决方案来解决这个问题? 请注意,应允许这些过程并行运行(除非在它们想要与设备通信的那些时刻)。显然可以按顺序运行这些程序(即一个接一个),但我不是在寻找这样的解决方案。
2) 实际上还有一个“Halt”按钮,可以停止所有正在运行的程序并向设备发送停止命令。但我也想不出实现这个功能(设置标志、发送退出信号等)。在这方面您能否也给我一些提示?
【问题讨论】:
-
关于错误消息
"QObject: Cannot create children for...",您可以使用qInstallMessageHandler安装您自己的消息处理程序并找出消息的确切来源。 -
@G.M.谢谢你提到这一点。但是您认为这会有所帮助吗,因为目前我知道它是由
wnd_->serial_->write('Command a1');行提出的(或者我错了吗?!)? -
您可以拥有一个管理函数指针列表(或一般函子)的线程,而不是为每个按钮单击创建一个新线程。当列表不为空时,它可以从列表中删除一个函数然后调用它,这样您的
onButtonAClicked()函数只需在按下某个键时将相关函数指针推到该列表上。 -
@today 是的,但消息可能不是直接来自
write电话。这实际上只是为了提供有关该消息的根本原因的更多信息。 -
请注意,大多数
QIODevice类的设计方式是您确实不需要使用线程。只需连接到他们的各种signals 并以您需要的任何方式处理它们。
标签: c++ multithreading qt serial-port serial-communication