从来自 QTcpSocket 的数据流中连续运行复杂算法的最佳 Qt 线程解决方案是啥?
Posted
技术标签:
【中文标题】从来自 QTcpSocket 的数据流中连续运行复杂算法的最佳 Qt 线程解决方案是啥?【英文标题】:What is the best Qt Threading solution for running a complex algorithm continuously from a stream of data coming over a QTcpSocket?从来自 QTcpSocket 的数据流中连续运行复杂算法的最佳 Qt 线程解决方案是什么? 【发布时间】:2017-10-27 15:51:28 【问题描述】:我在目前的工作中继承了一个复杂的程序,并正在寻求减少来自 QTcpSocket 的数据流中的图像闪烁。
程序接收连续的数据流,对其进行处理,然后使用paintEvent将其绘制在屏幕上。
处理函数基于信号/槽连接运行,其中信号是来自QTcpSocket的readyread(),槽是数据处理函数。流是连续的,因此该信号/插槽会根据传入的数据不断触发和更新屏幕上绘制的图像。
图像不断闪烁,我假设主事件循环中的处理可能会干扰数据流,所以我的想法是将数据处理函数放在自己的线程中。这个数据处理功能是如此彻底地集成到程序的其他功能中,所以此时对数据流进行子类化以便我可以应用 QThread 并不是一个解决方案,而是对整个程序的完全重组,需要花费大量时间.
所以我的想法是像这样使用 QtConcurrent:
void MainWindow::getDataThread() //implemented as a slot
wpFuture = QtConcurrent::run(this, &MainWindow::getData);
其中getData()是连接到readyread()信号的数据处理函数:
connect(tcpSocket2, SIGNAL(readyRead()), this, SLOT(getData()));
所以我将 SLOT(getData()) 替换为 SLOT(getDataThread()) 以允许数据处理函数在从全局线程池获取的新线程上运行。由于流是连续的,我相信每次运行 getData 处理函数时它都会不断地分配一个新线程。它似乎确实减少了闪烁,但在大约 30 到 60 秒后,程序随机崩溃,没有特定的标注。
所以我的问题是:有没有更好的方法来线程化我的数据处理函数,而不对数据流进行子类化?我在这种特定情况下实施 QtConcurrent 的想法/理解是否错误?
谢谢。
【问题讨论】:
通常闪烁通过双缓冲解决:doc.qt.io/archives/qq/qq06-flicker-free.html 另外,你确定同时从多个线程调用 MainWindow::getData 是安全的吗?如果您的程序曾经是单线程的,那么它可能不是线程安全的。 谢谢,我会检查双缓冲。关于多线程,每次在套接字上检测到数据时,都会执行 QtConcurrent::run。据我了解,一旦 QtConcurrent 在线程上运行函数并且函数完成,它会将线程返回到全局线程池,因此每次迭代都会将线程分配给函数,释放线程,分配线程,释放。这意味着任何时候只有一个线程会为 MainWindow::getData 函数运行。我认为这不是最好的方法。 【参考方案1】:根据您的评论,我认为您对线程池的理解是错误的。
线程池中有多个线程。每次调用QtConcurrent::run
时,都会从全局线程池中获取一个空闲线程,并交给一个任务(MainWindow::getData
)。如果您多次调用QtConcurrent::run
而不是每次MainWindow::getData
将在(可能)不同的线程中执行。如果线程池中当前没有可用的线程,您的任务将排队并在稍后可用时交给线程。这样,您可以同时运行多个任务,但会受到线程池中线程数的限制。
现在的问题是,MainWindow::getData
的设计可能不是线程安全的。多次调用QtConcurrent::run(this, &MainWindow::getData);
可能会导致数据争用。
如果您想要一个单独的单线程来处理数据,那么只需使用QThread
(无需“子类化”任何东西):
// A thread and its context are created only once
QThread thread;
QObject context;
context.moveToThread(&thread);
// ...
QObject::connect(tcpSocket2, &QTcpSocket::readyRead, &context, [this] ()
this->getData();
, Qt::QueuedConnection);
thread.start()
现在只要context
对象还活着并且每次QTcpSocket::readyRead
被释放时thread
都在运行 - lambda 就会被执行。
还是要注意不要让你的工作线程和你的主线程在getData
发生冲突。
【讨论】:
所以一旦QtConcurrent被分配运行的函数(在本例中为getData())完成后,它不会将线程释放回线程池吗? 它确实释放了线程。但是哪里保证当你第二次调用QtConcurrent::run
时,之前的getData()
调用已经完成了?还是我错过了什么?
我假设(因为我在文档中找不到)如果我将 readyread() 信号附加到自定义槽函数,则槽函数必须在信号之前完成可以再次开火。同样,我在文档中找不到它,所以这可能是我的误解。我会接受你的回答是正确的,因为它确实让我找到了正确的解决方案。最终,我应该更多地研究双缓冲而不是线程,但是您还为我提供了一个线程示例,我不需要对任何东西进行子类化。感谢您的帮助!以上是关于从来自 QTcpSocket 的数据流中连续运行复杂算法的最佳 Qt 线程解决方案是啥?的主要内容,如果未能解决你的问题,请参考以下文章
Qt基础之三十四:QTcpSocket和QTcpServer源码分析