从来自 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 线程解决方案是啥?的主要内容,如果未能解决你的问题,请参考以下文章

QTcpSocket的连续发送数据和连续接收数据

Qt基础之三十四:QTcpSocket和QTcpServer源码分析

QTcpSocket重连方式

当主线程无限循环运行时,服务器没有收到用 QTcpSocket::write 写入的字节?

QTcpSocket 从服务器接收数据

QTcpSocket 模拟 netcat '对话'