• C++及QT的线程学习


    目录

    一. 线程学习

    二. 学习线程当中,得到的未知。

    1. 了解以下MainWindow和main的关系

    2. [=]()匿名函数 有函数体,没有函数名.

    3. join和detach都是用来管理线程的生命周期的,它们的区别在于线程结束和资源的回收。

    4. operator()() 仿函数

    5. try-catch的使用以及细节。处理异常,try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。

    6. C++创建多线程

    1).普通函数的多线程创建方式

    2).lsmbda表达式方式创建多线程

    3).仿函数方式创建多线程

    4).类成员函数创建多线程


    一. 线程学习

    首先,我们任意建一个QT工程,按下按键让数字递增。

    1. void Widget::on_pushButton_clicked()
    2. {
    3. int i = 0;
    4. for(;;)
    5. {
    6. ui->lcdNumber->display(QString::number(i++));
    7. sleep(1);
    8. }
    9. }

    但是我们在槽里这样写的话,移动窗口或者按下按键会报错,因为我们既要绘制窗口,又要响应窗口移动的操作,还要执行自加的逻辑,当执行到这个自加的逻辑就已经很忙了,它就没有功夫去调用显示逻辑了。我们可以加一个Debug查看一下。

    1. void Widget::on_pushButton_clicked()
    2. {
    3. int i = 0;
    4. for(;;)
    5. {
    6. qDebug() << i;
    7. ui->lcdNumber->display(QString::number(i++));
    8. sleep(1);
    9. }
    10. }

    可以发现操作台是有数在走的,但是却没有显示。

    这时候我们就得采用多线程。那么我们使用join还是detach呢,因为主线程也要执行,不可能等待子线程执行,而且主线程本身就是一个循环,比如return a.exec();主进程不会退出,所以使用detach。

    1. void Widget::on_pushButton_clicked()
    2. {
    3. std::thread my_thread(&Widget::showInfo,this);
    4. my_thread.detach();
    5. }
    6. void Widget::showInfo()
    7. {
    8. int i = 0;
    9. for(;;)
    10. {
    11. qDebug() << i;
    12. ui->lcdNumber->display(QString::number(i++));
    13. sleep(1);
    14. }
    15. }

    因为是在栈中定义的my_thread,所以不需要担心资源回收的问题,函数一结束,my_thread就销毁了,不用担心线程回收的问题。

    这样,我们点击按钮开始计数之后就不会卡死。

    当然QT中封装了一个多线程的类,叫QThread。刚刚我们写的线程是没有退出的逻辑的,所以接下来我们使用QThread。

    定义一个自定义的类,继承QThread并重写run()方法,在里面写线程执行的逻辑,定义一个信号。

    1. #include "my_thrad.h"
    2. #include <QDebug>
    3. My_Thrad::My_Thrad(QObject *parent) : QThread(parent)
    4. {
    5. }
    6. void My_Thrad::run()
    7. {
    8. int i = 0;
    9. for(;;)
    10. {
    11. qDebug() << i;
    12. emit threadSignal(i++);
    13. this->sleep(1);
    14. if (i > 10)
    15. {
    16. break;
    17. }
    18. }
    19. }
    1. #ifndef MY_THRAD_H
    2. #define MY_THRAD_H
    3. #include
    4. #include
    5. class My_Thrad : public QThread
    6. {
    7. Q_OBJECT
    8. public:
    9. explicit My_Thrad(QObject *parent = nullptr);
    10. ~My_Thrad()//析构函数
    11. {
    12. qDebug() << "线程退出了,并回收了线程空间";
    13. }
    14. protected:
    15. void run() override;
    16. signals:
    17. void threadSignal(int val);
    18. };
    19. #endif // MY_THRAD_H

    这里我们添加了一个析构函数,是判断新建线程能不能结束,而要保证关闭窗口的时候,线程仍然能够执行,实现一个安全可靠的退出,我们就要:

    1. #include "widget.h"
    2. #include "ui_widget.h"
    3. Widget::Widget(QWidget *parent)
    4. : QWidget(parent)
    5. , ui(new Ui::Widget)
    6. {
    7. ui->setupUi(this);
    8. my_thread = new My_Thrad(this);//this父对象就是main.cpp中的w
    9. connect(my_thread,&My_Thrad::threadSignal,[=](int val){
    10. ui->lcdNumber->display(QString::number(val));
    11. });
    12. }
    13. Widget::~Widget()
    14. {
    15. delete ui;
    16. my_thread->exit();
    17. my_thread->wait();//等待线程执行完毕,然后才退出
    18. delete my_thread;
    19. // // my_thread->deleteLater();//它是依赖于某个对象,还依赖于主事件循环存在的情况下,才是有效的,所以这样会内存泄露
    20. }
    21. void Widget::on_pushButton_clicked()
    22. {
    23. my_thread->start();//使用start间接调用的run方法
    24. }

    因此我们在~Widget()析构函数中写上述代码,实现等待线程执行完毕,才退出,并删除my_thread,输出”线程退出了,并回收了线程空间。

    二. 学习线程当中,得到的未知。

    1. 了解以下MainWindow和main的关系

    main()函数,非窗体程序入口函数

    Mainwindow函数,是窗体程序的入口函数

    2. [=]()匿名函数 有函数体,没有函数名.

    3. join和detach都是用来管理线程的生命周期的,它们的区别在于线程结束和资源的回收。

    join函数会阻塞当前线程,直到被调用join()的线程(子线程)执行完毕并退出,在这个过程中,调用join()的线程会一直等待,直到被等待的线程退出。如果没有调用join函数。被等待的线程退出后,它的资源不会被回收,这可能会导致内存泄漏。

    有时候我们不知道是否已经join()系统提供了一个joinable()来判断是否已经join()

    使用detach会让线程在后台运行,这就意味着与主线程不能直接交互了,分离后的线程不能join

    但使用detach时,要注意主进程运行的时间,不然可能线程还没执行完,主进程就结束了

    4. operator()() 仿函数

    5. try-catch的使用以及细节。处理异常,try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。

    1. try{
    2. //可疑代码
    3. //将异常生成对象的异常对象传递给catch块
    4. }catch(异常){
    5. //对异常进行处理
    6. }finally{
    7. } //可以没有finally

    6. C++创建多线程

    1).普通函数的多线程创建方式

    1. #include <thread>
    2. #include <iostream>
    3. #include <unistd.h>
    4. #include <string>
    5. using namespace std;
    6. void showInfo()
    7. {
    8. int i = 0;
    9. for(;;)
    10. {
    11. cout << i++ << endl;
    12. sleep(10000);
    13. }
    14. }
    15. void print(const string &s)
    16. {
    17. cout<<"hello thread!"<<endl;
    18. cout<<s<<endl;
    19. }
    20. int main()
    21. {
    22. /*
    23. thread my_thread(&showInfo);
    24. my_thread.join();
    25. my_thread.detach();
    26. */
    27. cout<<"main thread begin!"<< endl;
    28. string s = "hello world";
    29. thread t(&print,s);
    30. //thread t(&print);
    31. t.join();
    32. cout<<"main thread end!"<<endl;
    33. return 0;
    34. }

    2).lsmbda表达式方式创建多线程

    1. #include <iostream>
    2. #include <thread>
    3. #include <string>
    4. using namespace std;
    5. int main()
    6. {
    7. cout<<"main begin"<<endl;
    8. thread t(
    9. [](string s)-> void{
    10. cout<<"hello world!"<<endl;
    11. cout<<s<<endl;
    12. },
    13. "abc"
    14. );
    15. t.join();
    16. cout<<"main end"<<endl;
    17. return 0;
    18. }

    3).仿函数方式创建多线程

    1. #include <iostream>
    2. #include <thread>
    3. #include <string>
    4. using namespace std;
    5. class MyThread{
    6. public:
    7. void operator()()
    8. {
    9. cout<<"Hello World!"<<endl;
    10. }
    11. };
    12. int main()
    13. {
    14. cout<<"main begin"<<endl;
    15. MyThread mt;
    16. thread t(mt);
    17. t.join();
    18. cout<<"main end"<<endl;
    19. return 0;
    20. }

    4).类成员函数创建多线程

    1. #include <iostream>
    2. #include <thread>
    3. #include <string>
    4. using namespace std;
    5. class MyThread{
    6. public:
    7. void print(const string &s)
    8. {
    9. cout<<"Hello World!"<<endl;
    10. cout<<s<<endl;
    11. }
    12. };
    13. int main()
    14. {
    15. cout<<"main begin"<<endl;
    16. MyThread mt;
    17. thread t(&MyThread::print,&mt,"ac") ;
    18. t.join();
    19. cout<<"main end"<<endl;
    20. return 0;
    21. }

  • 相关阅读:
    关于#php#的问题:在安装完wampserver后点击wampserver后弹出这个窗口
    Prometheus系列第一篇一监控+面板+告警三剑客部署
    【Unity Shader】Unity中自阴影优化方案
    TypeScript文件的编译执行
    动物园规则怪谈【逻辑】
    后端常用的Linux命令大全
    做项目管理需要哪些技能?
    低轨互联网产业发展探究
    uniapp获取公钥、MD5,‘keytool‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
    基数排序的简单理解
  • 原文地址:https://blog.csdn.net/m0_66746512/article/details/138098560