C++及QT的线程学习

news/2024/9/23 20:11:30/

目录

一. 线程学习

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

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

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

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

4. operator()() 仿函数

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

6. C++创建多线程

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

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

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

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


一. 线程学习

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

void Widget::on_pushButton_clicked()
{int i = 0;for(;;){ui->lcdNumber->display(QString::number(i++));sleep(1);}
}

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

void Widget::on_pushButton_clicked()
{int i = 0;for(;;){qDebug() << i;ui->lcdNumber->display(QString::number(i++));sleep(1);}
}

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

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

void Widget::on_pushButton_clicked()
{std::thread my_thread(&Widget::showInfo,this);my_thread.detach();
}void Widget::showInfo()
{int i = 0;for(;;){qDebug() << i;ui->lcdNumber->display(QString::number(i++));sleep(1);}
}

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

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

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

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

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

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

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);my_thread = new My_Thrad(this);//this父对象就是main.cpp中的wconnect(my_thread,&My_Thrad::threadSignal,[=](int val){ui->lcdNumber->display(QString::number(val));});
}Widget::~Widget()
{delete ui;my_thread->exit();my_thread->wait();//等待线程执行完毕,然后才退出delete my_thread;
//   // my_thread->deleteLater();//它是依赖于某个对象,还依赖于主事件循环存在的情况下,才是有效的,所以这样会内存泄露
}void Widget::on_pushButton_clicked()
{my_thread->start();//使用start间接调用的run方法
}

因此我们在~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块中发生的异常。

try{
//可疑代码
//将异常生成对象的异常对象传递给catch块
}catch(异常){
//对异常进行处理
}finally{
} //可以没有finally

6. C++创建多线程

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

#include <thread>
#include <iostream>
#include <unistd.h>
#include <string>
using namespace std;
void showInfo()
{int i = 0;for(;;){cout << i++ << endl;sleep(10000);}
}void print(const string &s)
{cout<<"hello thread!"<<endl;cout<<s<<endl;
}int main()
{/*thread my_thread(&showInfo);my_thread.join();my_thread.detach();*/cout<<"main thread begin!"<< endl;string s = "hello world";thread t(&print,s);//thread t(&print);t.join();cout<<"main thread end!"<<endl;return 0;
}

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

#include <iostream>
#include <thread>
#include <string>
using namespace std;int main()
{cout<<"main begin"<<endl;thread t([](string s)-> void{cout<<"hello world!"<<endl;cout<<s<<endl;},"abc");t.join();cout<<"main end"<<endl;return 0;
}

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

#include <iostream>
#include <thread>
#include <string>
using namespace std;
class MyThread{
public:void operator()(){cout<<"Hello World!"<<endl;}
};int main()
{cout<<"main begin"<<endl;MyThread mt;thread t(mt);t.join();cout<<"main end"<<endl;return 0;
}

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

#include <iostream>
#include <thread>
#include <string>
using namespace std;
class MyThread{
public:void print(const string &s){cout<<"Hello World!"<<endl;cout<<s<<endl;}
};int main()
{cout<<"main begin"<<endl;MyThread mt;thread t(&MyThread::print,&mt,"ac") ;t.join();cout<<"main end"<<endl;return 0;
}


http://www.ppmy.cn/news/1437960.html

相关文章

深度学习每周学习总结P5(运动鞋识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 –来自百度网盘超级会员V5的分享 数据链接 提取码&#xff1a;9539 目录 0. 总结1. 数据导入及处理部分2. 加载数据集3.模型构建部分3.1…

问答机器人学习资料

longchain 5 分钟内搭建一个免费问答机器人&#xff1a;Milvus LangChain-腾讯云开发者社区-腾讯云 chatglm大模型 基于本地知识的问答机器人langchain-ChatGLM 大语言模型实现方法详解_python_脚本之家 探索QRobot&#xff1a;智能对话与聊天机器人的开源奇缘-CSDN博客 如何…

Python爬虫(入门版)

1、爬虫是什么 简单的来说&#xff1a;就是用程序获取网络上数据。 2、爬虫的原理 如果要获取网络上数据&#xff0c;我们要给爬虫一个网址&#xff08;程序中通常叫URL&#xff09;&#xff0c;爬虫发送一个HTTP请求给目标网页的服务器&#xff0c;服务器返回数据给客户端&am…

kotlin根据文件的filePath转化为uri

方法实现 使用File类来创建一个文件对象&#xff0c;然后通过FileProvider来获取文件的URI import android.content.Context import android.net.Uri import androidx.core.content.FileProvider import java.io.Filefun getFileUri(context: Context, filePath: String): Ur…

1分钟掌握 Python 函数参数

任何编程语言函数都是非常重要的一部分&#xff0c;而在进行函数调用时&#xff0c;了解函数的参数传递方式是非常有必要的。Python中支持哪些传参方式呢&#xff1f; Python中的传参方式是比较灵活的&#xff0c;主要包括以下六种&#xff1a; 按照位置传参按照关键字传参默…

vue2知识点————(vue插槽,透传 Attributes )

vue 插槽 插槽&#xff08;slot&#xff09;是一种强大的特性&#xff0c;允许在组件的模板中定义带有特定用途的“插槽”&#xff0c;然后在组件的使用者中填充内容。插槽能够使组件更加灵活&#xff0c;让组件的结构更容易复用和定 具名插槽&#xff08;Named Slots&#x…

Java openrasp记录-01

例子1 https://github.com/anbai-inc/javaweb-expression 一个hook ognl、spel、MVEL表达式注入的例子 用的是asm5进行字节码修改 采用premain进行插桩&#xff0c;重写transform方法 expClassList是要hook的类&#xff0c;这里定义在MethodHookDesc 这里判断hook点通过类名…

02 贪吃蛇

前言 呵呵 这是不知道 在哪里看到的 别人做的一个贪吃蛇 因此 也把我 之前的 贪吃蛇 移植上来了 当然 这个不过是为了 简单的入门了解, 呵呵 然后 c版本的贪吃蛇 需要先移植成 c 版本, 然后 再根据 单片机相关 设计调整 比如 led 点阵的输出, 比如 c99 语法的一些不兼容…