• 62630阅读
  • 63回复

QT多线程示例+一种通用高并发数据处理最简单思路 [复制链接]

上一主题 下一主题
在线liudianwu
 

只看楼主 正序阅读 楼主  发表于: 2015-01-31
— 本帖被 XChinux 执行加亮操作(2016-04-18) —
平时的项目程序中,经常需要处理多个串口和网络发送过来的数据,而且数据量还比较大,9600的波特率每秒钟至少1000个字节的数据需要处理并反映到界面上,一开始直接和UI主线程同一个线程,在x86的机器上跑还没问题,毕竟X86的机器最少主频也不会低于1.6G,但是如果数据量再更大或者到了ARM上跑,直接UI卡住不动,想到的解决办法就是用多线程,一个线程负责收数据,一个线程负责处理数据,当协议一样的时候,如果需要将数据解析从串口改为网络端口监听的数据,以前的办法是重新写一个tcp通信进行处理,这个并不是非常合理的办法,毕竟解析协议是一样的,所以自己总结了一个通用的数据处理思路:各种数据接收后排队的形式存入一个全局变量,单独开辟一个线程从这个全局变量中读取第一个数据,处理完则移除第一个数据,Qt中的链表直接提供了一个takeFirst函数,用起来非常爽!用while循环读取,在读取的时候加锁,这样的话就不会冲突了。
雏形:
全局变量文件
  1. #ifndef APP_H
  2. #define APP_H
  3. #include "qstring.h"
  4. #include "qstringlist.h"
  5. class App
  6. {
  7. public:    
  8.     static QStringList list;
  9. };
  10. #endif // APP_H

  1. #include "app.h"
  2. QStringList App::list=QStringList();

独立处理数据线程:
  1. #ifndef TEST_H
  2. #define TEST_H
  3. #include "qthread.h"
  4. #include "qmutex.h"
  5. class Thread : public QThread
  6. {
  7.     Q_OBJECT
  8. public:
  9.     Thread();
  10.     ~Thread();
  11.     void stop();
  12. protected:
  13.     void run();
  14. private:
  15.     QMutex mutex;
  16.     volatile bool stopped;
  17. signals:
  18.     void readOne(QString txt);
  19. };
  20. #endif // TEST_H

  1. #include "thread.h"
  2. #include "app.h"
  3. Thread::Thread()
  4. {
  5. stopped=false;
  6. }
  7. Thread::~Thread()
  8. {
  9. }
  10. void Thread::stop()
  11. {
  12.     stopped=true;
  13. }
  14. void Thread::run()
  15. {
  16.     while(!stopped){
  17.         mutex.lock();
  18.         if (App::list.count()>0){            
  19.             QString txt=App::list.takeFirst();
  20.             emit readOne(txt);
  21.         }
  22.         mutex.unlock();
  23.         msleep(1);//不加这句CPU占用率高达50%
  24.     }
  25.     stopped=false;
  26. }

主界面:
  1. #ifndef WIDGET_H
  2. #define WIDGET_H
  3. #include <QWidget>
  4. #include "thread.h"
  5. #include "qtimer.h"
  6. namespace Ui {
  7. class frmMain;
  8. }
  9. class frmMain : public QWidget
  10. {
  11.     Q_OBJECT
  12. public:
  13.     explicit frmMain(QWidget *parent = 0);
  14.     ~frmMain();
  15. private slots:
  16.     void writeOne();
  17.     void readOne(QString txt);
  18.     void on_btnAppend_clicked();
  19.     void on_btnThread_clicked();
  20.     void on_btnTimer_clicked();
  21. private:
  22.     Ui::frmMain *ui;
  23.     QTimer *timer;
  24.     Thread *thread;
  25. };
  26. #endif // WIDGET_H

  1. #include "frmmain.h"
  2. #include "ui_frmmain.h"
  3. #include "app.h"
  4. #include "qdatetime.h"
  5. #include "qdesktopwidget.h"
  6. #define _TIME_ qPrintable (QTime::currentTime().toString("now : hh:mm:ss:zzz"))
  7. frmMain::frmMain(QWidget *parent) :
  8.     QWidget(parent),
  9.     ui(new Ui::frmMain)
  10. {
  11.     ui->setupUi(this);
  12.     this->showMaximized();
  13.     timer=new QTimer(this);
  14.     timer->setInterval(50);
  15.     connect(timer,SIGNAL(timeout()),this,SLOT(writeOne()));
  16.     thread=new Thread;
  17.     connect(thread,SIGNAL(readOne(QString)),this,SLOT(readOne(QString)));
  18. }
  19. frmMain::~frmMain()
  20. {
  21.     delete ui;
  22. }
  23. void frmMain::writeOne()
  24. {
  25.     App::list.append(_TIME_);
  26. }
  27. void frmMain::readOne(QString txt)
  28. {
  29.     ui->txtOut->append(txt);
  30. }
  31. void frmMain::on_btnAppend_clicked()
  32. {
  33.     App::list.append(ui->txtIn->text());
  34. }
  35. void frmMain::on_btnThread_clicked()
  36. {
  37.     if (ui->btnThread->text()=="start thread"){
  38.         thread->start();
  39.         ui->btnThread->setText("stop thread");
  40.         ui->txtOut->append("start thread ok");
  41.     }else{
  42.         thread->stop();
  43.         ui->btnThread->setText("start thread");
  44.         ui->txtOut->append("stop thread ok");
  45.     }
  46. }
  47. void frmMain::on_btnTimer_clicked()
  48. {
  49.     if (ui->btnTimer->text()=="start timer"){
  50.         timer->start();
  51.         ui->btnTimer->setText("stop timer");
  52.         ui->txtOut->append("start timer ok");
  53.     }else{
  54.         timer->stop();
  55.         ui->btnTimer->setText("start timer");
  56.         ui->txtOut->append("stop timer ok");
  57.     }
  58. }

为了模拟大量数据,我这里开了50毫秒的定时器定时产生当前时间字符串的数据存入全局变量,然后放置了几个按钮用于手动添加字符串和开始停止线程及定时器。



欢迎提出批评建议以及指点!谢谢!
代码下载 ThreadTool.zip (6 K) 下载次数:1615
12条评分好评度+2贡献值+3金钱+5威望+2
drabel 好评度 +1 优秀文章,支持!n神马都是浮云 2020-04-01
drabel 贡献值 +1 优秀文章,支持!n神马都是浮云 2020-04-01
drabel 威望 +1 优秀文章,支持!n神马都是浮云 2020-04-01
drabel 金钱 +1 优秀文章,支持!n神马都是浮云 2020-04-01
qinlian2010 好评度 +1 nice 2019-09-19
qinlian2010 贡献值 +1 nice 2019-09-19
qinlian2010 威望 +1 nice 2019-09-19
qinlian2010 金钱 +1 nice 2019-09-19
leee101 金钱 +1 - 2019-04-18
听雨陌行 贡献值 +1 - 2019-04-11
12
欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线是小熊猫

只看该作者 63楼 发表于: 03-28
大神很多啊,很有收获感谢
离线shokokawaii

只看该作者 62楼 发表于: 2023-10-22
QFluentWidgets 是基于 Qt 的 Fluent Designer 组件库,内置超过 160 个开箱即用的 Fluent Designer 组件,支持亮暗主题无缝切换和自定义主题色。搭配所见即所得的 Fluent Designer 软件,只需拖拖拽拽,不用编写一行 QSS,就能快速搭建现代化界面。有意者可联系邮箱或者 QQ~
官网:https://qfluentwidgets.com/zh/ (需要科学上网)
邮箱:shokokawaii@foxmail.com
QQ:1953658489
离线dutyxl

只看该作者 61楼 发表于: 2023-06-16
离线xliuzz

只看该作者 60楼 发表于: 2022-03-17

只看该作者 59楼 发表于: 2020-04-29
刚入坑,感觉啥也不会啊。学习了。感谢。
离线☆black

只看该作者 58楼 发表于: 2020-02-25
感谢分享!
离线clickto

只看该作者 57楼 发表于: 2019-09-19
刚好可以借鉴一下。
离线qinlian2010

只看该作者 56楼 发表于: 2019-09-19
回 hezf 的帖子
hezf:[图片]
使用两个信号量做了一个环形区,存储信息,随写随读
1、不必加锁
2、不必sleep
当然,如果数据量非常大,可以增大缓冲区的大小
....... (2015-08-25 15:16) 

压缩包为何是损坏的
离线hanhai

只看该作者 55楼 发表于: 2019-09-09
非常好,谢谢
离线jsxyhyj

只看该作者 54楼 发表于: 2019-08-21
回 勤为径也 的帖子
勤为径也:上面两位的例子都非常好!
但是,都无可避免的出现了内存泄漏的问题!
其实,我也遇到了内存泄漏的同样问题无法解决。
不知道是不是Qt的bug。
敬请高手指点!!! (2018-01-01 22:22) 

ui->txtOut->clear();
在适当位置加上这个,内存就正常了。
离线searchcai

只看该作者 53楼 发表于: 2019-07-31

离线yangzi8000

只看该作者 52楼 发表于: 2019-05-22
nice
离线yjnn1116

只看该作者 51楼 发表于: 2019-04-07


不错。学习学习
离线夜风朗朗

只看该作者 50楼 发表于: 2019-02-20

离线chenyulxp

只看该作者 49楼 发表于: 2019-01-14
666
离线iamtsl

只看该作者 48楼 发表于: 2018-12-03
学习到知识了~
离线hanheyfon

只看该作者 47楼 发表于: 2018-10-17
学习学习收藏
离线sevencat

只看该作者 46楼 发表于: 2018-07-18
处理数据多不要一个一个的read,
一般是这样:
std::list<int> putqueue;放数据
取数据的时候
std::list<int> readq;
{
    lock(xxx)
    readq.swap(putqueue);
}
handle(readq);
战争就是和平,自由就是奴役,愚味就是力量
离线盒子6255

只看该作者 45楼 发表于: 2018-06-29
下载下来学习一下感谢楼主的分享,么么哒
离线三只小鸡

只看该作者 44楼 发表于: 2018-06-26
什么时候才可以像你们这么优秀
离线益达张哥

只看该作者 43楼 发表于: 2018-06-06
看看
离线twodiamond

只看该作者 42楼 发表于: 2018-05-26

离线wayxl184

只看该作者 41楼 发表于: 2018-05-11
厉害厉害。。。。。。。。。。。。。。。。。。。
离线hanheyfon

只看该作者 40楼 发表于: 2018-05-07
    
离线zxnzxn

只看该作者 39楼 发表于: 2018-03-02
回 liudianwu 的帖子
liudianwu:在windows下用的50毫秒的定时器产生时间字符串信息,不是很准,可能和底层有关,估计QT还有更精确的定时器类,但是在ubuntu上跑,连定时10毫秒都很精准,一毫秒不差,到了ARM上精确度又下降了。郁闷! (2015-01-31 17:24) 

另外,还想请教你,在windows下,实时性要求较高的通讯,有没有什么好的建议。如50ms或者更快。
离线zxnzxn

只看该作者 38楼 发表于: 2018-03-02
回 liudianwu 的帖子
liudianwu:在windows下用的50毫秒的定时器产生时间字符串信息,不是很准,可能和底层有关,估计QT还有更精确的定时器类,但是在ubuntu上跑,连定时10毫秒都很精准,一毫秒不差,到了ARM上精确度又下降了。郁闷! (2015-01-31 17:24) 

将QTimer的类型设置为Qt::PreciseTimer,就很准了,我测试了。
离线zxnzxn

只看该作者 37楼 发表于: 2018-03-01
请问一下:你时钟设置为50ms,可以打印出来的时间却是62,这是怎么回事。
离线z99monster

只看该作者 36楼 发表于: 2018-01-22
正在头疼这块儿怎么弄,学习了~感谢!
离线核心科技

只看该作者 35楼 发表于: 2018-01-03
回 liudianwu 的帖子
liudianwu:在windows下用的50毫秒的定时器产生时间字符串信息,不是很准,可能和底层有关,估计QT还有更精确的定时器类,但是在ubuntu上跑,连定时10毫秒都很精准,一毫秒不差,到了ARM上精确度又下降了。郁闷! (2015-01-31 17:24) 

你自定义一个多媒体定时器,毫秒级,妥妥的。
离线勤为径也

只看该作者 34楼 发表于: 2018-01-01
上面两位的例子都非常好!
但是,都无可避免的出现了内存泄漏的问题!
其实,我也遇到了内存泄漏的同样问题无法解决。
不知道是不是Qt的bug。
敬请高手指点!!!
离线t1029901995

只看该作者 33楼 发表于: 2017-11-28
回 hezf 的帖子
hezf:[图片]
使用两个信号量做了一个环形区,存储信息,随写随读
1、不必加锁
2、不必sleep
当然,如果数据量非常大,可以增大缓冲区的大小
....... (2015-08-25 15:16) 

还是有些bug
离线chengruoqie

只看该作者 32楼 发表于: 2017-11-22
离线calvin.huang

只看该作者 31楼 发表于: 2017-11-21
感激感激~
离线allens

只看该作者 30楼 发表于: 2017-11-04
离线msy7807887

只看该作者 29楼 发表于: 2017-11-03
学习了
离线mattino

只看该作者 28楼 发表于: 2017-10-31
学习了,多谢高手
离线ninicaoyuan

只看该作者 27楼 发表于: 2017-10-26
回 hezf 的帖子
hezf:
[图片]
使用两个信号量做了一个环形区,存储信息,随写随读
1、不必加锁
2、不必sleep
当然,如果数据量非常大,可以增大缓冲区的大小
.......

有BUG样,程序会卡死在只开timer那
离线寒阳

只看该作者 26楼 发表于: 2017-08-28
  
离线sunbelt_liu

只看该作者 25楼 发表于: 2017-08-01
回 旷性怡情 的帖子
旷性怡情:请教一下楼主 ,在线程中取出tex的时候有加锁,但是在定时函数中存进去的时候没有加锁,这样做是有什么作用的??
 (2015-02-26 11:21) 

对呀,楼主,读的过程始终只有一个线程在运行,怎么会有冲突呢?
快速回复
限100 字节
 
上一个 下一个