目录
一. 线程学习
二. 学习线程当中,得到的未知。
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中的w
connect(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;
}