我是C ++和Qt的新手,我在实现一个好的而不是过于复杂的多线程通信环境方面遇到了一些麻烦。
基本上我有3个线程,一个用于GUI
,另一个用于处理由USB连接到PC的device
发送的更新,另一个用于处理{{1}获取的信息{}}和device
control
和device
来改变他们的状态。所以基本上是3个主题:GUI
,GUI
和device
。
我的第一种方法是让control
使用USB发送的信息填充其私人成员,并使用一些device
方法转换此数据并将其返回(使用互斥锁确保数据仍然有效)。问题是当get()
调用control
中的get()
方法时,它不会返回任何新内容(我希望方法甚至永远不会返回,因为线程被锁定在另一个方法中,但是它们返回并没有新信息,并且在device
方法中也没有触发断点。
Qt进行线程间通信的常用方法是使用信号和插槽,但信号和插槽的问题是当一个线程正在处理并且它有一个Slot时,如果发送了一些Signal,则不会执行此Slot。即使我可以设法使用信号和插槽来触发新的数据更新,我担心会有很多信号被发送,因为设备更新速度非常快,我也有很多数据类型,使用QAtomicInt对许多数据类型都不会有用。所以我的一般问题是哪个是使线程共享数据并仍然继续运行无限流程循环的最佳方法?
我的目标的一个很好的例子就是:
控制线程:
get()
设备线程:
while(true){
angle = device.getAngle(); //device is a member of control object and is running in a separate thread
doCalculations(angle);
}
我不是在这个例子中放置互斥锁等,只是一个基本的功能。
这里要求的是我如何开始我的线程:
void process(){
while(true)
usbRead(data, size_of_data);
}
short getAngle(){
return (data[0] << 8 | data[1]);
}
答案 0 :(得分:2)
处理此问题的方法不止一种:
1)手动睡眠并在某些时间段自行醒来检查是否有变化。这可以用行话来进行民意调查。
2)使用线程的事件循环来处理信号和插槽等事件。
我会建议后者,因为前者可能有睡眠时无法做任何事情的缺陷,你可能会弄错。更重要的是,你也会突然失去伟大的信号和插槽机制,你的代码会变得更加耦合,而不是必要的。
至于正确地执行后者,您需要确保正确执行Qt事件循环,这由QThread
的更高版本保证。话虽如此,请阅读以下文档,因为我认为有必要注意:
int QThread::exec() [protected]
进入事件循环并等待直到调用exit(),返回传递给exit()的值。如果通过quit()调用exit(),则返回的值为0。
此函数旨在从run()中调用。有必要调用此函数来启动事件处理。
您也可以通过混合上述两种方法来完成中间第三种解决方案,即:调用下面的方法以明确确保所有事件都得到处理。这将是一种半(a)同步方式(取决于你如何看待它),因为你需要在从睡眠中醒来进行轮询时这样做,而不是手动处理轮询,你可以使用这种方便方法
根据指定的标志处理调用线程的所有挂起事件,直到没有其他事件要处理。
当程序忙于执行长时间操作(例如复制文件)时,您可以偶尔调用此功能。
如果您正在运行一个连续调用此函数的本地循环,而没有事件循环,则不会处理DeferredDelete事件。这可能会影响窗口小部件的行为,例如QToolTip,依赖DeferredDelete事件正常运行。另一种方法是从该本地循环中调用sendPostedEvents()。
调用此函数仅处理调用线程的事件。
答案 1 :(得分:1)
让我们说你的设备是这样的:
while(keep_running){
handle_incoming_data();
}
在这种情况下,您没有时间处理此线程中的任何其他内容,因为此循环根本不会退出。但是,您可以将循环更改为以下构造:
public slots:
void single_step(){
if(keep_running){
handle_incoming_data();
QTimer::singleShot(0, this, SLOT(single_step()));
}
}
或
public slots:
void start_work(){
my_timer->start(0);
}
void stop_work(){
my_timer->stop();
}
其中my_timer
是QTimer*
并连接到正确的广告位(在本例中为handle_incoming_data()
。请注意,0毫秒超时具有特殊含义:
作为一种特殊情况,只要处理了窗口系统事件队列中的所有事件,超时为0的
QTimer
就会超时。这可以用来做繁重的工作,同时提供一个活泼的用户界面:QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(processOneThing())); timer->start();
有关详细信息,请参阅section about QTimer
。