• 62652阅读
  • 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
离线liudianwu

只看该作者 1楼 发表于: 2015-01-31
在windows下用的50毫秒的定时器产生时间字符串信息,不是很准,可能和底层有关,估计QT还有更精确的定时器类,但是在ubuntu上跑,连定时10毫秒都很精准,一毫秒不差,到了ARM上精确度又下降了。郁闷!
欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线旷性怡情

只看该作者 2楼 发表于: 2015-02-26
请教一下楼主 ,在线程中取出tex的时候有加锁,但是在定时函数中存进去的时候没有加锁,这样做是有什么作用的??
1条评分好评度+1
sunbelt_liu 好评度 +1 - 2017-08-01
离线electri

只看该作者 3楼 发表于: 2015-02-26
事实上,你可以建立一个圆形缓冲区队列,使用QMutex和QWaitCondition构建一个闭锁队列,在队列没有数据时,队列的取数据接口popup()一直闭锁,一旦队列push()进去一个数据才返回第一个数据,就可以避免    

msleep(1);//不加这句CPU占用率高达50%  

这句话了

使用闭锁队列的线程如果要退出时,随便塞一个空数据进去,让popup()返回,然后判断退出标志退出线程
离线liudianwu

只看该作者 4楼 发表于: 2015-02-27
回 electri 的帖子
electri:事实上,你可以建立一个圆形缓冲区队列,使用QMutex和QWaitCondition构建一个闭锁队列,在队列没有数据时,队列的取数据接口popup()一直闭锁,一旦队列push()进去一个数据才返回第一个数据,就可以避免    
msleep(1);//不加这句CPU占用率高达50%   .. (2015-02-26 17:02) 

嗯,真是遇见高手啊!谢谢建议,我试试看!
欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线hezf

只看该作者 5楼 发表于: 2015-08-25
ThreadTool.rar (5 K) 下载次数:1433
使用两个信号量做了一个环形区,存储信息,随写随读
1、不必加锁
2、不必sleep
当然,如果数据量非常大,可以增大缓冲区的大小
总是看你的界面,非常感激
不知道能否帮上忙
真希望多几个像你这样的愿意分享的大神
2条评分好评度+1贡献值+1
drabel 好评度 +1 优秀文章,支持!n神马都是浮云 2020-04-01
drabel 贡献值 +1 优秀文章,支持!n神马都是浮云 2020-04-01
none
离线ruben_30

只看该作者 6楼 发表于: 2016-03-28
学习学习
离线hehui

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


只看该作者 8楼 发表于: 2016-03-30
不需要用 stopped控制,使用!isInterruptionRequested()即可!停止的时候调用requestInterruption();
wait();
博客地址:https://waleon.blog.csdn.net
微信公众号:高效程序员
QQ 群:242790253
微信群:加微信(iwaleon),邀请入群。

承接各种 C++/Qt/QML 项目,专业的团队,一流的服务,不二的选择!
离线ap0405209

只看该作者 9楼 发表于: 2016-03-30
很好的帖子  学习到知识了~~谢谢
离线amoi3000

只看该作者 10楼 发表于: 2016-04-03
感谢分享~!!!!!!!!!!!!!!!!!!
离线sunnlboy

只看该作者 11楼 发表于: 2016-04-07
感谢分享,正在学习!
离线xiaoniede

只看该作者 12楼 发表于: 2016-04-18
遇到同样的问题,感谢大神分享。学习啦!
离线t1029901995

只看该作者 13楼 发表于: 2016-04-21
        
离线zhangcan

只看该作者 14楼 发表于: 2016-06-13
学习了
离线p1111234

只看该作者 15楼 发表于: 2016-06-26
很好的帖子  学习到知识了~~谢谢
离线galaxyz

只看该作者 16楼 发表于: 2016-06-28
离线liudianwu

只看该作者 17楼 发表于: 2016-06-28
回 一去丶二三里 的帖子
一去丶二三里:不需要用 stopped控制,使用!isInterruptionRequested()即可!停止的时候调用requestInterruption();
wait(); (2016-03-30 08:59) 

requestInterruption()是qt5才有的方法!qt4还是要自己写stopped变量控制开始和停止的。
欢迎关注微信公众号:Qt实战/Qt入门和进阶(各种开源作品、经验整理、项目实战技巧,专注Qt/C++软件开发,视频监控、物联网、工业控制、嵌入式软件、国产化系统应用软件开发) QQ:517216493  WX:feiyangqingyun  QQ群:751439350
离线cycloveu

只看该作者 18楼 发表于: 2016-07-12
生产者消费者模型
大道至简 悟在天成
离线dubuzherui

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

这个例子写的不错,
电力系统Qt开发

只看该作者 20楼 发表于: 2016-11-22
又来膜拜大神了,感觉界面上跑的时钟可以整到这个里面,不会因为被阻塞而造成走时不准
离线latte79

只看该作者 21楼 发表于: 2016-12-09
不错 不错,非常的实用!
hezf 热心人不少
离线kqh1120

只看该作者 22楼 发表于: 2017-01-17
学习到知识了
离线hunterzf

只看该作者 23楼 发表于: 2017-02-19
谢谢楼主
离线yzysgz

只看该作者 24楼 发表于: 2017-07-28
感谢分享!
离线sunbelt_liu

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

对呀,楼主,读的过程始终只有一个线程在运行,怎么会有冲突呢?
离线寒阳

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

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

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

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

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

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

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

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

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

还是有些bug
离线勤为径也

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

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

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

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

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

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

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

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

另外,还想请教你,在windows下,实时性要求较高的通讯,有没有什么好的建议。如50ms或者更快。
快速回复
限100 字节
 
上一个 下一个